• Noip前的大抱佛脚----字符串


    字符串

    Tags:Noip前的大抱佛脚

    经验

    用FFT求解字符串匹配问题

    • 一一对应

    把其中一个(Reverse)后,对于每个字符跑一遍FFT,打上(Tag)
    如果在某个位置上有串长个(Tag)那便是匹配上了一处

    • 模糊匹配

    (Fuzzy Search) 在跑(FFT)前把模糊门限值的区间内全部置为1,然后同样的操作

    两(多)串DP时状态合并

    插入AC自动机,老套路了

    最长公共子序列转LIS

    求两个串的最长公共子序列,把第二个串的每个值映射到第一个串上 该值的位置
    然后对第二个串求LIS即可(最长公共子串就是第二个串的最长连续段)

    位运算最大值

    异或最大值:建Trie贪心
    与最大值:建Trie贪心(1的儿子siz大于2则走)/高维前缀和逐位贪心
    或最大值:高维前缀和逐位贪心

    但是求(x+B)^A的最大值呢(SCOI2016美味)(当然&操作也是一样的,这种题通常值域很小)
    同样贪心地做
    开值域线段树,贪心到某一位,需要该位为0或者1,则对应地可以算出x的范围,查询是否有在这个范围之内的x即可

    挂链哈希

    期望(O(1)),当然支持查询多个关键字

    const int Mo=YYB;
    struct HashTable
    {
    	struct Line{int next,val;}a[Mo];
    	int head[Mo],cnt;
    	void reset() {memset(head,0,sizeof(head));cnt=0;}
    	void Add(int p,int val) {a[++cnt]=(Line){head[p],val};head[p]=cnt;}
    	int Query(int x)
    		{
    			for(int i=head[x%Mo];i;i=a[i].next)
    				if(a[i].val==x) return 1;return 0;
    		}
    }Hash;
    

    哈希处理回文串

    正反哈希前缀和即可求出区间哈希值,然后查询起点到回文中心的正哈希和回文中心到终点的反哈希即可

    树哈希

    例:一棵无根树的本质不同的独立集个数((k)棵相同子树方案为(x)则乘一个可重组合)

    字符串模板库

    KMP

    【模板】KMP字符串匹配
    用于两串匹配问题,做法是对子串求next后匹配母串,复杂度(O(n+m))

    const int N=1e6+10;
    char s1[N],s2[N];
    int nxt[N];
    int main()
    {
    	scanf("%s%s",s1+1,s2+1);
    	int l1=strlen(s1+1),l2=strlen(s2+1);
    	for(int i=2;i<=l2;i++)
    	{
    		int j=nxt[i-1];
    		while(j&&s2[j+1]!=s2[i]) j=nxt[j];
    		nxt[i]=(s2[j+1]==s2[i])+j;
    	}
    	for(int i=1,j=0;i<=l1;i++)
    	{
    		while(j&&s2[j+1]!=s1[i]) j=nxt[j];
    		if(s2[j+1]==s1[i]) j++;
    		if(j==l2) printf("%d
    ",i-l2+1),j=nxt[j];
    	}
    	for(int i=1;i<=l2;i++) printf("%d ",nxt[i]);
    	return puts(""),0;
    }
    

    最小循环表示

    工艺
    (O(n))求一个环从某点断开按一定方向形成的字典序最小的链

    int i,j=2,k,l,p,s[610000];
    int main()
    {
    	cin>>l;for(i=1;i<=l;i++) cin>>s[i],s[i+l]=s[i];
    	for(i=1;j<=l&&i<=l&&k<=l;)
    	{
    		if(s[i+k]==s[j+k]) {k++;continue;}
    		s[i+k]<s[j+k]?j+=k+1:i+=k+1;
    		if(i==j) i++;k=0;
    	}
    	for(;p<l;p++) cout<<s[min(i,j)+p]<<" ";
    }
    
    

    Mancher

    【模板】manacher算法
    求出以每个位置为中心的最长回文串,复杂度(O(n)),证明:每次操作要么不动(while),要么给两个单调的指针至少(+1)

    
    const int N=3e7+10;
    char s[N],t[N];
    int l,p[N],R,C,Ans;
    int main()
    {
    	scanf("%s",t+1);
    	for(int i=1,len=strlen(t+1);i<=len;i++)
    		s[++l]='#',s[++l]=t[i];s[++l]='#';
    	for(int i=1;i<=l;i++)
    	{
    		p[i]=i<=R?min(p[C*2-i],R-i):1;
    		while(s[i+p[i]]==s[i-p[i]]&&i+p[i]<=l&&i-p[i]>=1) p[i]++;
    		if(i+p[i]-1>R) R=i+p[i]-1,C=i;
    		Ans=max(Ans,p[i]-1);
    	}
    	cout<<Ans<<endl;
    }
    

    AC自动机

    【模板】AC自动机(加强版)
    用于处理多串匹配单串等多串问题,复杂度(O(n*26))
    方式是先建(Trie),求出(fail)指针后建成(Trie)

    int n,node,fail[N],ch[N][26];
    queue<int> Q;
    void Insert(char *s,int id)
    {
    	int x=0,l=strlen(s+1);
    	for(int i=1;i<=l;i++)
    	{
    		int &p=ch[x][s[i]-'a'];
    		if(!p) p=++node;x=p;
    	}
    }
    void Get_fail()
    {
    	for(int i=0;i<26;i++) if(ch[0][i]) Q.push(ch[0][i]);
    	while(!Q.empty())
    	{
    		int x=Q.front();Q.pop();
    		for(int i=0;i<26;i++)
    			if(ch[x][i]) fail[ch[x][i]]=ch[fail[x]][i],Q.push(ch[x][i]);
    			else ch[x][i]=ch[fail[x]][i];
    	}
    }
    

    后缀数组

    【模板】后缀排序
    用于处理字符串后缀的东西(不过这东西Noip不会考,省选题倒是经常出现)
    复杂度(O(nlogn)),基于一种倍增和桶排的思想对后缀排序

    
    const int N=1e6+10;
    int m=300,t[N],x[N],y[N],rk[N],h[N],SA[N],l;char s[N];
    int cmp(int i,int j,int k) {return y[i]==y[j]&&y[i+k]==y[j+k];}
    void Sort()
    {
        for(int i=1;i<=m;i++) t[i]=0;
        for(int i=1;i<=l;i++) t[x[i]]++;
        for(int i=1;i<=m;i++) t[i]+=t[i-1];
        for(int i=l;i>=1;i--) SA[t[x[y[i]]]--]=y[i];
    }
    void Get_SA()
    {
        for(int i=1;i<=l;i++) x[i]=s[i],y[i]=i;Sort();
        for(int k=1,p=0;k<=l;k<<=1)
        {
            for(int i=l-k+1;i<=l;i++) y[++p]=i;
            for(int i=1;i<=l;i++) if(SA[i]>k) y[++p]=SA[i]-k;
            Sort();swap(x,y);x[SA[1]]=p=1;
            for(int i=2;i<=l;i++) x[SA[i]]=cmp(SA[i],SA[i-1],k)?p:++p;
            if(p==l) break;m=p;p=0;
        }
        for(int i=1;i<=l;i++) rk[SA[i]]=i;
        for(int i=1,j=0;i<=l;i++)
        {
            while(s[i+j]==s[SA[rk[i]-1]+j]) j++;
            h[rk[i]]=j;if(j) j--;
        }
    }
    int main()
    {
        scanf("%s",s+1);l=strlen(s+1);Get_SA();
        for(int i=1;i<=l;i++) printf("%d ",SA[i]);
    }
    

    后缀自动机

    【模板】后缀自动机
    用于处理子串的问题。这家伙比SA好写复杂度还优秀适用范围还广些
    不过Noip还是不会考,复杂度(O(n))

    
    const int N=2e6+10;
    int l,lst=1,node=1,t[N],A[N],len[N],fa[N];
    int ch[N][26],siz[N];char s[N];
    void Extend(int c)
    {
    	int f=lst,p=++node;lst=p;
    	len[p]=len[f]+1;siz[p]=1;
    	while(f&&!ch[f][c]) ch[f][c]=p,f=fa[f];
    	if(!f) {fa[p]=1;return;}
    	int x=ch[f][c],y=++node;
    	if(len[f]+1==len[x]) {fa[p]=x;node--;return;}
    	len[y]=len[f]+1;fa[y]=fa[x];fa[x]=fa[p]=y;
    	memcpy(ch[y],ch[x],sizeof(ch[y]));
    	while(f&&ch[f][c]==x) ch[f][c]=y,f=fa[f];
    }
    int main()
    {
    	scanf("%s",s+1);l=strlen(s+1);
    	for(int i=1;i<=l;i++) Extend(s[i]-'a');
    }
    
  • 相关阅读:
    jQuery 插件为什么要return this.each()
    Android中TextView setText int 报错
    局部打印插件 jquery.PrintArea.js
    Jdbc Url 设置allowMultiQueries为true和false时底层处理机制研究
    mysql-unsha1:在未知密码情况下,登录任意MYSQL数据库
    Wireshark实战之抓包MySQL Java客户端
    linux 文件句柄数查看命令
    Java1.8 JDK源码中,对两个类进行 按位与 操作是什么意思
    linux下面覆盖文件,如何实现直接覆盖,不提示
    比特币算法——SHA256算法介绍
  • 原文地址:https://www.cnblogs.com/xzyxzy/p/9903901.html
Copyright © 2020-2023  润新知