zoukankan      html  css  js  c++  java
  • 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');
    }
    
  • 相关阅读:
    Swift入门篇-Hello World
    Swift入门篇-swift简介
    Minecraft 插件 world edit 的cs 命令
    搭建本地MAVEN NEXUS 服务
    MC java 远程调试 plugin 开发
    企业内部从零开始安装docker hadoop 提纲
    javascript 命令方式 测试例子
    ca des key crt scr
    JSF 抽象和实现例子 (函数和属性)
    form 上传 html 代码
  • 原文地址:https://www.cnblogs.com/xzyxzy/p/9903901.html
Copyright © 2011-2022 走看看