【算法总结】字符串相关

【KMP算法】

〖模板代码

 1     n=strlen(a);m=strlen(b);
 2     for(int i=1;i<m;i++)
 3     {
 4         int j=f[i];
 5         while(j&&b[i]!=b[j])j=f[j];
 6         f[i+1]=b[i]==b[j]?j+1:0;
 7     }
 8     int j=0;
 9     for(int i=0;i<n;i++)
10     {
11         while(j&&b[j]!=a[i])j=f[j];
12         if(b[j]==a[i])j++;
13         if(j==m)printf("%d\n",i-m+1);
14     }
View Code

〖相关题目

1.【bzoj1355】[Baltic2009]Radio Transmission

题意:有一个字符串由某个字符串不断自我连接形成的字符串,这个字符串是不确定的,现在想知道它的最短长度是多少。

分析:hzwerの博客

 1 #include<cstdio>
 2 #include<algorithm> 
 3 #include<cstring>
 4 #define LL long long
 5 using namespace std;
 6 const int N=1e6+5;
 7 int n,j,fail[N];
 8 char s[N];
 9 int read()
10 {
11     int x=0,f=1;char c=getchar();
12     while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
13     while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
14     return x*f;
15 }
16 int main()
17 {
18     n=read();
19     scanf("%s",s+1);
20     for(int i=2;i<=n;i++)
21     {
22         while(j&&s[j+1]!=s[i])j=fail[j];
23         if(s[j+1]==s[i])j++;
24         fail[i]=j;
25     }
26     printf("%d",n-fail[n]);
27     return 0;
28 }
View Code

2.【Codeforces Round #269 (Div. 2)】D. MUH and Cube Walls

题意:给你一个长度为n(1<=n<=2e5)的一排积木,长度分别为a[](1<=a[]<=1e9)。 有一个长度为m(1<=m<=2e5)的一排积木,长度分别为b[](1<=b[]<=1e9)。问你,第一排积木有多少个位点i,使得[i+0,i+m-1]这一段积木,之间增减幅度与b[]的整体增减幅度相同。 增减幅度肯定产生于相邻的积木之间。 

分析:hzwerの博客

 1 #include<cstdio>
 2 #include<algorithm> 
 3 #include<cstring>
 4 #define LL long long
 5 using namespace std;
 6 const int N=2e5+5;
 7 int n,m,x,y,ans,a[N],b[N],fail[N];
 8 int read()
 9 {
10     int x=0,f=1;char c=getchar();
11     while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
12     while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
13     return x*f;
14 }
15 int main()
16 {
17     n=read()-1;m=read()-1;
18     x=read();
19     for(int i=1;i<=n;i++)y=read(),a[i]=y-x,x=y;
20     x=read();
21     for(int i=1;i<=m;i++)y=read(),b[i]=y-x,x=y;
22     if(m==0){printf("%d",n+1);return 0;}
23     if(n<m){printf("0");return 0;}
24     int j=0;
25     for(int i=2;i<=m;i++)
26     {
27         while(j&&b[j+1]!=b[i])j=fail[j];
28         if(b[j+1]==b[i])j++;
29         fail[i]=j;
30     }
31     j=0;
32     for(int i=1;i<=n;i++)
33     {
34         while(j&&b[j+1]!=a[i])j=fail[j];
35         if(b[j+1]==a[i])j++;
36         if(j==m)ans++,j=fail[j];
37     }
38     printf("%d",ans);
39     return 0;
40 }
View Code

3.【bzoj3670】[Noi2014]动物园

题意:求出一个num数组一一对于字符串S的前i个字符构成的子串,既是它的后缀同时又是它的前缀,并且该后缀与该前缀不重叠,将这种字符串的数量记作num[i]。输出Π(num[i]+1)。

分析:num指的是数量而不是长度。

 1 #include<cstdio>
 2 #include<algorithm> 
 3 #include<cstring>
 4 #define LL long long
 5 using namespace std;
 6 const int N=1e6+5;
 7 const int mod=1e9+7;
 8 int T,n,ans,j,fail[N],num[N];
 9 char s[N];
10 int read()
11 {
12     int x=0,f=1;char c=getchar();
13     while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
14     while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
15     return x*f;
16 }
17 int main()
18 {
19     T=read();
20     while(T--)
21     {
22         scanf("%s",s+1);
23         n=strlen(s+1);ans=1;
24         num[1]=1;j=0;
25         for(int i=2;i<=n;i++)
26         {
27             while(j&&s[j+1]!=s[i])j=fail[j];
28             if(s[j+1]==s[i])j++;
29             fail[i]=j;num[i]=num[j]+1;
30         }
31         j=0;
32         for(int i=1;i<=n;i++)
33         {
34             while(j&&s[j+1]!=s[i])j=fail[j];
35             if(s[j+1]==s[i])j++;
36             while(j*2>i)j=fail[j];
37             ans=1ll*ans*(num[j]+1)%mod;
38         }
39         printf("%d\n",ans);
40     }
41     return 0;
42 }
View Code

4.【bzoj1009】[HNOI2008]GT考试

题意:阿申准备报名参加GT考试,准考证号为N位数X1X2....Xn(0<=Xi<=9),他不希望准考证号上出现不吉利的数字。他的不吉利数学A1A2...Am(0<=Ai<=9)有M位,不出现是指X1X2...Xn中没有恰好一段等于A1A2...Am。阿申想知道不出现不吉利数字的号码有多少种。

分析:hzwerの博客

 1 #include<cstdio>
 2 #include<algorithm> 
 3 #include<cstring>
 4 #define LL long long
 5 using namespace std;
 6 const int N=25;
 7 int n,m,mod,j,ans,num[N],fail[N];
 8 char ch[N];
 9 struct node{int a[N][N];node(){memset(a,0,sizeof(a));}}a,b;
10 int read()
11 {
12     int x=0,f=1;char c=getchar();
13     while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
14     while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
15     return x*f;
16 }
17 node operator * (node a,node b)
18 {
19     node c;
20     for(int i=0;i<m;i++)
21         for(int j=0;j<m;j++)
22             for(int k=0;k<m;k++)
23                 c.a[i][j]=(c.a[i][j]+a.a[i][k]*b.a[k][j]%mod)%mod;
24     return c;
25 }
26 int main()
27 {
28     n=read();m=read();mod=read();scanf("%s",ch+1);
29     for(int i=1;i<=m;i++)num[i]=ch[i]-'0';
30     for(int i=0;i<m;i++)a.a[i][i]=1;
31     for(int i=2;i<=m;i++)
32     {
33         while(j&&num[j+1]!=num[i])j=fail[j];
34         if(num[j+1]==num[i])j++;
35         fail[i]=j;
36     }
37     for(int i=0;i<m;i++)
38         for(int j=0;j<=9;j++)
39         {
40             int t=i;
41             while(t&&num[t+1]!=j)t=fail[t];
42             if(num[t+1]==j)t++;
43             if(t!=m)b.a[i][t]=(b.a[i][t]+1)%mod;
44         }
45     while(n)
46     {
47         if(n&1)a=a*b;
48         b=b*b;n>>=1;
49     }
50     for(int i=0;i<m;i++)ans=(ans+a.a[0][i])%mod;
51     printf("%d",ans);
52     return 0;
53 }
View Code

【后缀数组】

〖模板代码

 1 void build()
 2 {
 3     int *x=t1,*y=t2;
 4     for(int i=0;i<n;i++)c[x[i]]++;
 5     for(int i=1;i<m;i++)c[i]+=c[i-1];
 6     for(int i=n-1;i>=0;i--)sa[--c[x[i]]]=i;
 7     for(int k=1;k<=n;k<<=1)
 8     {
 9         int p=0;
10         for(int i=n-k;i<n;i++)y[p++]=i;
11         for(int i=0;i<n;i++)if(sa[i]>=k)y[p++]=sa[i]-k;
12         memset(c,0,sizeof(c));
13         for(int i=0;i<n;i++)c[x[y[i]]]++;
14         for(int i=1;i<m;i++)c[i]+=c[i-1];
15         for(int i=n-1;i>=0;i--)sa[--c[x[y[i]]]]=y[i];
16         swap(x,y);
17         p=1;x[sa[0]]=0;
18         for(int i=1;i<n;i++)
19             x[sa[i]]=y[sa[i-1]]==y[sa[i]]&&y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++;
20         if(p>=n)break;
21         m=p;
22     }
23 }
View Code

【AC自动机】

〖相关资料

AC自动机总结及板子(不带指针)

〖模板代码

 1 struct Trie
 2 {
 3     int root,cnt;
 4     int son[N*55][26],fail[N*55],num[N*55];
 5     bool mark[N*55];
 6     void init()
 7     {
 8         cnt=1;root=0;
 9         memset(son,0,sizeof(son));
10         memset(fail,0,sizeof(fail));
11         memset(num,0,sizeof(num));
12         memset(mark,0,sizeof(mark));
13     }
14     int idx(char c){return c-'a';}
15     void ins(char s[])
16     {
17         int len=strlen(s),cur=root;
18         for(int i=0;i<len;i++)
19         {
20             int id=idx(s[i]);
21             if(!son[cur][id])son[cur][id]=cnt++;
22             cur=son[cur][id];
23         }
24         num[cur]++;
25     }
26     void build()
27     {
28         int head=0,tail=0,q[N*55],now,nex;
29         for(int i=0;i<26;i++)
30         {
31             now=son[root][i];
32             if(now)q[tail++]=now;
33         } 
34         while(head!=tail)
35         {
36             now=q[head++];
37             for(int i=0;i<26;i++)
38             {
39                 nex=son[now][i];
40                 if(!nex){son[now][i]=son[fail[now]][i];continue;}
41                 q[tail++]=nex;
42                 fail[nex]=son[fail[now]][i];
43             }
44         }
45     }
46     void query(char s[])
47     {
48         int len=strlen(s),j=root,c;
49         for(int i=0;i<len;i++)
50         {
51             mark[j]=true;c=idx(s[i]);
52             while(j&&!son[j][c])j=fail[j];
53             j=son[j][c];
54             if(!mark[j])
55                 for(int tmp=j;tmp;tmp=fail[tmp])
56                     ans+=num[tmp],num[tmp]=0;
57         }
58         printf("%d\n",ans);
59     }
60 }AC;
View Code

〖相关题目

1.【hdu2222】Keywords Search

题意:给出n个单词和1个模式串,求有多少个单词在模式串中出现。

分析:AC自动机裸题

 1 #include<cstdio>
 2 #include<algorithm> 
 3 #include<cstring>
 4 #define LL long long
 5 using namespace std;
 6 const int N=1e4+5;
 7 int T,n,ans;
 8 char s[N][55],ss[N*100];
 9 struct Trie
10 {
11     int root,cnt;
12     int son[N*55][26],fail[N*55],num[N*55];
13     bool mark[N*55];
14     void init()
15     {
16         cnt=1;root=0;
17         memset(son,0,sizeof(son));
18         memset(fail,0,sizeof(fail));
19         memset(num,0,sizeof(num));
20         memset(mark,0,sizeof(mark));
21     }
22     int idx(char c){return c-'a';}
23     void ins(char s[])
24     {
25         int len=strlen(s),cur=root;
26         for(int i=0;i<len;i++)
27         {
28             int id=idx(s[i]);
29             if(!son[cur][id])son[cur][id]=cnt++;
30             cur=son[cur][id];
31         }
32         num[cur]++;
33     }
34     void build()
35     {
36         int head=0,tail=0,q[N*55],now,nex;
37         for(int i=0;i<26;i++)
38         {
39             now=son[root][i];
40             if(now)q[tail++]=now;
41         } 
42         while(head!=tail)
43         {
44             now=q[head++];
45             for(int i=0;i<26;i++)
46             {
47                 nex=son[now][i];
48                 if(!nex){son[now][i]=son[fail[now]][i];continue;}
49                 q[tail++]=nex;
50                 fail[nex]=son[fail[now]][i];
51             }
52         }
53     }
54     void query(char s[])
55     {
56         int len=strlen(s),j=root,c;
57         for(int i=0;i<len;i++)
58         {
59             mark[j]=true;c=idx(s[i]);
60             while(j&&!son[j][c])j=fail[j];
61             j=son[j][c];
62             if(!mark[j])
63                 for(int tmp=j;tmp;tmp=fail[tmp])
64                     ans+=num[tmp],num[tmp]=0;
65         }
66         printf("%d\n",ans);
67     }
68 }AC;
69 void work()
70 {
71     scanf("%d",&n);AC.init();ans=0;
72     for(int i=1;i<=n;i++)scanf("%s",s[i]),AC.ins(s[i]);
73     AC.build();scanf("%s",ss);AC.query(ss);
74 }
75 int main()
76 {
77     scanf("%d",&T);
78     while(T--)work();
79     return 0;
80 }
View Code

2.【hdu2896】病毒侵袭

题意:给出n个单词和m个模式串,输出在每个模式串中出现的单词编号。

分析:AC自动机裸题

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int N=1e5+5;
 6 const int M=1e4+5;
 7 int n,m,ans,id[505];
 8 bool mark[N];
 9 char s[M];
10 struct Trie
11 {
12     int cnt,son[N][128],fail[N],num[N],q[N];
13     void ins(int v)
14     {
15         int cur=0,len=strlen(s);
16         for(int i=0;i<len;i++)
17         {
18             int now=s[i];
19             if(!son[cur][now])son[cur][now]=cnt++;
20             cur=son[cur][now];
21         }
22         num[cur]++;id[v]=cur;
23     }
24     void build()
25     {
26         int head=0,tail=0,now,nex;
27         for(int i=0;i<128;i++)
28         {
29             now=son[0][i];
30             if(now)q[tail++]=now;
31         }
32         while(head!=tail)
33         {
34             now=q[head++];
35             for(int i=0;i<128;i++)
36             {
37                 nex=son[now][i];
38                 if(!nex){son[now][i]=son[fail[now]][i];continue;}
39                 q[tail++]=nex;
40                 fail[nex]=son[fail[now]][i];
41             }
42         }
43     }
44     void query(int v)
45     {
46         int len=strlen(s),j=0,c;
47         bool flag=false;
48         memset(mark,0,sizeof(mark));
49         for(int i=0;i<len;i++)
50         {
51             c=s[i];j=son[j][c];
52             for(int tmp=j;tmp;tmp=fail[tmp])
53                 if(num[tmp])flag=true,mark[tmp]=true;
54         }
55         if(!flag)return;
56         printf("web %d:",v);
57         for(int i=1;i<=n;i++)
58             if(mark[id[i]])printf(" %d",i);
59         printf("\n");ans++;
60     }
61 }AC;
62 int main()
63 {
64     AC.cnt=1;
65     scanf("%d",&n);
66     for(int i=1;i<=n;i++)scanf("%s",s),AC.ins(i);
67     AC.build();
68     scanf("%d",&m);
69     for(int i=1;i<=m;i++)scanf("%s",s),AC.query(i);
70     printf("total: %d",ans);
71     return 0;
72 }
View Code

3.【hdu3065】病毒侵袭持续中

题意:给出n个单词和1个模式串,求每个单词在模式串中出现的次数。

分析:AC自动机裸题

 1 #include<cstdio>
 2 #include<algorithm> 
 3 #include<cstring>
 4 #define LL long long
 5 using namespace std;
 6 const int N=1e3+5;
 7 const int M=5e4+5;
 8 const int L=2e6+5;
 9 int n,times[N];
10 char s[N][55],ch[L];
11 struct Trie
12 {
13     int cnt,son[M][27],fail[M],num[M],q[M];
14     void init()
15     {
16         cnt=1;
17         memset(son,0,sizeof(son));
18         memset(fail,0,sizeof(fail));
19         memset(num,0,sizeof(num));
20     }
21     int idx(char c)
22     {
23         if(c>='A'&&c<='Z')return c-'A';
24         return 26;
25     }
26     void ins(int v)
27     {
28         int cur=0,len=strlen(s[v]);
29         for(int i=0;i<len;i++)
30         {
31             int now=idx(s[v][i]);
32             if(!son[cur][now])son[cur][now]=cnt++;
33             cur=son[cur][now];
34         }
35         num[cur]=v;
36     }
37     void build()
38     {
39         int head=0,tail=0,now,nex;
40         for(int i=0;i<=26;i++)
41         {
42             now=son[0][i];
43             if(now)q[tail++]=now;
44         }
45         while(head!=tail)
46         {
47             now=q[head++];
48             for(int i=0;i<=26;i++)
49             {
50                 nex=son[now][i];
51                 if(!nex){son[now][i]=son[fail[now]][i];continue;}
52                 q[tail++]=nex;
53                 fail[nex]=son[fail[now]][i];
54             }
55         }
56     }
57     void query()
58     {
59         int len=strlen(ch),j=0,c;
60         for(int i=0;i<len;i++)
61         {
62             c=idx(ch[i]);j=son[j][c];
63             for(int tmp=j;tmp;tmp=fail[tmp])
64                 if(num[tmp])times[num[tmp]]++;
65         }
66         for(int i=1;i<=n;i++)
67             if(times[i])printf("%s: %d\n",s[i],times[i]);
68     }
69 }AC;
70 int main()
71 {
72     while(scanf("%d",&n)==1)
73     {
74         memset(times,0,sizeof(times));
75         AC.init();
76         for(int i=1;i<=n;i++)scanf("%s",s[i]),AC.ins(i);
77         AC.build();scanf("%s",ch);AC.query();
78     }
79     return 0;
80 }
View Code

4.【bzoj2938】病毒

题意:已知某些确定的01串是病毒代码。如果某段代码中不存在任何一段病毒代码,那么我们就称这段代码是安全的。已知所有的病毒代码段,试问,是否存在一个无限长的安全的二进制代码。

分析:hzwerの博客

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int N=3e4+5;
 6 int n;
 7 char s[N];
 8 struct Trie
 9 {
10     int cnt,son[N][2],fail[N],q[N];
11     bool ed[N],in[N],vis[N];
12     int idx(char c){return c-'0';}
13     void ins()
14     {
15         scanf("%s",s);
16         int cur=0,len=strlen(s);
17         for(int i=0;i<len;i++)
18         {
19             int now=idx(s[i]);
20             if(!son[cur][now])son[cur][now]=cnt++;
21             cur=son[cur][now];
22         }
23         ed[cur]=true;
24     }
25     void build()
26     {
27         int head=0,tail=0,now,nex;
28         for(int i=0;i<2;i++)
29         {
30             now=son[0][i];
31             if(now)q[tail++]=now;
32         }
33         while(head!=tail)
34         {
35             now=q[head++];
36             for(int i=0;i<2;i++)
37             {
38                 nex=son[now][i];
39                 if(!nex){son[now][i]=son[fail[now]][i];continue;}
40                 q[tail++]=nex;
41                 fail[nex]=son[fail[now]][i];
42                 ed[nex]|=ed[fail[nex]];
43             }
44         }
45     }
46     bool dfs(int x)
47     {
48         in[x]=true;
49         for(int i=0;i<2;i++)
50         {
51             int now=son[x][i];
52             if(in[now])return true;
53             if(ed[now]||vis[now])continue;
54             vis[now]=true;
55             if(dfs(now))return true;
56         }
57         in[x]=false;
58         return false;
59     }
60 }AC;
61 int main()
62 {
63     AC.cnt=1;
64     scanf("%d",&n);
65     for(int i=1;i<=n;i++)AC.ins();
66     AC.build();
67     if(AC.dfs(0))printf("TAK\n");
68     else printf("NIE\n");
69     return 0;
70 }
View Code

【后缀自动机】

〖注意事项

实现后缀排序时,记得t[nq].id=0。

〖模板代码

[普通SAM]

 1 struct SAM{int mx,fa,ch[26];}t[N<<1];
 2 void ins(int c)
 3 {
 4     int np=++size;
 5     t[np].mx=t[last].mx+1;
 6     int x=last;last=np;
 7     while(x&&!t[x].ch[c])t[x].ch[c]=np,x=t[x].fa;
 8     if(!x)t[np].fa=root;
 9     else
10     {
11         int y=t[x].ch[c];
12         if(t[y].mx==t[x].mx+1)t[np].fa=y;
13         else
14         {
15             int nq=++size;
16             t[nq]=t[y];
17             t[nq].mx=t[x].mx+1;
18             t[y].fa=t[np].fa=nq;
19             while(x&&t[x].ch[c]==y)t[x].ch[c]=nq,x=t[x].fa;
20         }
21     }
22 }
23 int main()
24 {
25     scanf("%s",s+1);n=strlen(s+1);
26     last=size=root=1;
27     for(int i=1;i<=n;i++)ins(s[i]-'a');
28     return 0;
29 }
View Code

[后缀排序]

 1 #include<cstdio>
 2 #include<algorithm> 
 3 #include<cstring>
 4 #include<cmath>
 5 #define LL long long
 6 using namespace std;
 7 const int N=15005;
 8 int n,size,root,last,tot,cnt,scnt;
 9 int c[26],sa[N],first[N<<1],rk[N<<1];
10 char ch[N]; 
11 bool vis[N<<1];
12 struct sam{int mx,fa,id,ch[26];}t[N<<1];
13 struct node{int x,y,v;}a[N<<1];
14 struct edge{int to,next;}e[N<<1];
15 void insert(int u,int v){e[++cnt]=(edge){v,first[u]};first[u]=cnt;}
16 void ins(int c,int pos)
17 {
18     int np=++size;
19     t[np].mx=t[last].mx+1;
20     t[np].id=pos;
21     int x=last;last=np;
22     while(x&&!t[x].ch[c])t[x].ch[c]=np,x=t[x].fa;
23     if(!x)t[np].fa=root;
24     else
25     {
26         int y=t[x].ch[c];
27         if(t[y].mx==t[x].mx+1)t[np].fa=y;
28         else
29         {
30             int nq=++size;t[nq]=t[y];
31             t[nq].mx=t[x].mx+1;t[nq].id=0;
32             t[y].fa=t[np].fa=nq;
33             while(x&&t[x].ch[c]==y)t[x].ch[c]=nq,x=t[x].fa;
34         }
35     }
36 }
37 void dfs(int x)
38 {
39     if(t[x].id)sa[++scnt]=t[x].id;
40     for(int i=first[x];i;i=e[i].next)dfs(e[i].to);
41 }
42 int main()
43 {
44     scanf("%d%s",&n,ch+1);
45     size=root=last=1;vis[1]=true;
46     for(int i=n;i>=1;i--)ins(ch[i]-'a',i);
47     for(int i=1;i<=size;i++)
48         if(!vis[i]&&t[i].id)
49             for(int pos=n,j=i;!vis[j];vis[j]=true,j=t[j].fa,--pos)
50             {
51                 pos=pos-t[j].mx+t[t[j].fa].mx+1;
52                 a[++tot]=(node){t[j].fa,j,ch[pos]-'a'};
53             }
54     for(int i=1;i<=tot;i++)c[a[i].v]++;
55     for(int i=1;i<26;i++)c[i]+=c[i-1];
56     for(int i=1;i<=tot;i++)rk[c[a[i].v]--]=i;
57     for(int i=tot;i>=1;i--)insert(a[rk[i]].x,a[rk[i]].y);
58     dfs(1);
59     for(int i=1;i<=n;i++)printf("%d\n",sa[i]);
60     return 0;
61 }
View Code

〖相关题目

1.【Luogu P3804】【模板】后缀自动机

题意:给定一个只包含小写字母的字符串SS,请你求出 SS 的所有出现次数不为 11 的子串的出现次数乘上该子串长度的最大值。

分析:后缀自动机裸题

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 #define LL long long
 5 using namespace std;
 6 const int N=1e6+5;
 7 int n,last,size,root,x;
 8 int sz[N<<1],c[N<<1],q[N<<1];
 9 LL ans;
10 char s[N];
11 struct SAM{int mx,fa,ch[26];}t[N<<1];
12 void ins(int c)
13 {
14     int np=++size;sz[np]=1;
15     t[np].mx=t[last].mx+1;
16     int x=last;last=np;
17     while(x&&!t[x].ch[c])t[x].ch[c]=np,x=t[x].fa;
18     if(!x)t[np].fa=root;
19     else
20     {
21         int y=t[x].ch[c];
22         if(t[y].mx==t[x].mx+1)t[np].fa=y;
23         else
24         {
25             int nq=++size;
26             t[nq]=t[y];
27             t[nq].mx=t[x].mx+1;
28             t[nq].fa=t[y].fa;
29             t[y].fa=t[np].fa=nq;
30             while(x&&t[x].ch[c]==y)t[x].ch[c]=nq,x=t[x].fa;
31         }
32     }
33 }
34 int main()
35 {
36     scanf("%s",s+1);n=strlen(s+1);
37     last=size=root=1;
38     for(int i=1;i<=n;i++)ins(s[i]-'a');
39     for(int i=1;i<=size;i++)c[t[i].mx]++;
40     for(int i=1;i<=size;i++)c[i]+=c[i-1];
41     for(int i=1;i<=size;i++)q[c[t[i].mx]--]=i;
42     for(int i=size;i>=1;i--)
43     {
44         x=q[i];sz[t[x].fa]+=sz[x];
45         if(sz[x]>1)ans=max(ans,1ll*sz[x]*t[x].mx);
46     }
47     printf("%lld",ans);
48     return 0;
49 }
View Code

2.【bzoj3238】[Ahoi2013]差异

题意:给定长度为n的小写字母字符串,令Ti表示以i开头的后缀,求Σ[Ti+Tj-2*lcp(Ti,Tj)],1<=i<j<=n。

分析:把串倒过来,两个后缀的lcp就是他们在Parent树上的LCA

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 #define LL long long
 5 using namespace std;
 6 const int N=1e6+5;
 7 int n,size,last,root,cnt;
 8 int first[N],sz[N];
 9 LL ans;
10 bool f[N];
11 char s[N];
12 struct SAM{int mx,fa,ch[26];}t[N];
13 struct edge{int to,next;}e[N];
14 void insert(int u,int v){e[++cnt]=(edge){v,first[u]};first[u]=cnt;}
15 void ins(int c)
16 {
17     int np=++size;f[np]=true;
18     t[np].mx=t[last].mx+1;
19     int x=last;last=np;
20     while(x&&!t[x].ch[c])t[x].ch[c]=np,x=t[x].fa;
21     if(!x)t[np].fa=root;
22     else
23     {
24         int y=t[x].ch[c];
25         if(t[y].mx==t[x].mx+1)t[np].fa=y;
26         else
27         {
28             int nq=++size;
29             t[nq]=t[y];
30             t[nq].mx=t[x].mx+1;
31             t[y].fa=t[np].fa=nq;
32             while(x&&t[x].ch[c]==y)t[x].ch[c]=nq,x=t[x].fa;
33         }
34     }
35 }
36 void dfs(int x)
37 {
38     sz[x]=f[x]?1:0;
39     for(int i=first[x];i;i=e[i].next)
40     {
41         int to=e[i].to;dfs(to);
42         ans+=1ll*t[x].mx*sz[x]*sz[to];
43         sz[x]+=sz[to];
44     }
45 }
46 int main()
47 {
48     scanf("%s",s+1);n=strlen(s+1);
49     last=size=root=1;
50     for(int i=n;i>=1;i--)ins(s[i]-'a');
51     for(int i=2;i<=size;i++)insert(t[i].fa,i);
52     dfs(1);printf("%lld",1ll*(n+1)*n/2*(n-1)-2*ans);
53     return 0;
54 }
View Code

3.【bzoj4032】[HEOI2015]最短不公共子串

题意:给两个小写字母串A,B,计算:(1) A的一个最短的子串,它不是B的子串;(2) A的一个最短的子串,它不是B的子序列;(3) A的一个最短的子序列,它不是B的子串;(4) A的一个最短的子序列,它不是B的子序列。

分析:后缀自动机+序列自动机

  1 #include<cstdio>
  2 #include<algorithm>
  3 #include<cstring>
  4 #define LL long long
  5 using namespace std;
  6 const int N=2005;
  7 const int inf=0x3f3f3f3f;
  8 int n,m,c,x,y,root,size,last,ans,now,sum;
  9 int pre[26],nexa[N][26],nexb[N][26],f[N][N<<1];
 10 char a[N],b[N];
 11 struct SAM{int mx,fa,ch[26];}t[N<<1];
 12 void insert(int c)
 13 {
 14     int np=++size;
 15     t[np].mx=t[last].mx+1;
 16     int x=last;last=np;
 17     while(x&&!t[x].ch[c])t[x].ch[c]=np,x=t[x].fa;
 18     if(!x)t[np].fa=root;
 19     else
 20     {
 21         int y=t[x].ch[c];
 22         if(t[y].mx==t[x].mx+1)t[np].fa=y;
 23         else
 24         {
 25             int nq=++size;t[nq]=t[y];
 26             t[nq].mx=t[x].mx+1;
 27             t[y].fa=t[np].fa=nq;
 28             while(x&&t[x].ch[c]==y)t[x].ch[c]=nq,x=t[x].fa;
 29         }
 30     }
 31 }
 32 void work1()
 33 {
 34     ans=n+1;
 35     for(int i=1;i<=n;i++)
 36     {
 37         now=root;sum=0;c=a[i+sum]-'a';
 38         while(i+sum<=n&&t[now].ch[c])
 39         {
 40             now=t[now].ch[c];sum++;
 41             c=a[i+sum]-'a';
 42         }
 43         if(i+sum!=n+1)ans=min(ans,sum+1);
 44     }
 45     if(ans==n+1)printf("-1\n");
 46     else printf("%d\n",ans);
 47 }
 48 void work2()
 49 {
 50     ans=n+1;
 51     for(int i=1;i<=n;i++)
 52     {
 53         now=0;sum=0;c=a[i+sum]-'a';
 54         while(i+sum<=n&&nexb[now][c])
 55         {
 56             now=nexb[now][c];sum++;
 57             c=a[i+sum]-'a';
 58         }
 59         if(i+sum!=n+1)ans=min(ans,sum+1);
 60     }
 61     if(ans==n+1)printf("-1\n");
 62     else printf("%d\n",ans);
 63 }
 64 void work3()
 65 {
 66     memset(f,0x3f,sizeof(f));
 67     f[0][1]=0;ans=n+1;
 68     for(int i=0;i<n;i++)
 69         for(int j=1;j<=size;j++)
 70         {
 71             if(f[i][j]==inf)continue;
 72             for(int k=0;k<26;k++)
 73                 if(x=nexa[i][k])
 74                 {
 75                     y=t[j].ch[k];
 76                     if(!y)ans=min(ans,f[i][j]+1);
 77                     else f[x][y]=min(f[x][y],f[i][j]+1);
 78                 }
 79         }
 80     if(ans==n+1)printf("-1\n");
 81     else printf("%d\n",ans);
 82 }
 83 void work4()
 84 {
 85     memset(f,0x3f,sizeof(f));
 86     f[0][0]=0;ans=n+1;
 87     for(int i=0;i<n;i++)
 88         for(int j=0;j<=m;j++)
 89         {
 90             if(f[i][j]==inf)continue;
 91             for(int k=0;k<26;k++)
 92                 if(x=nexa[i][k])
 93                 {
 94                     y=nexb[j][k];
 95                     if(!y)ans=min(ans,f[i][j]+1);
 96                     else f[x][y]=min(f[x][y],f[i][j]+1);
 97                 }
 98         }
 99     if(ans==n+1)printf("-1\n");
100     else printf("%d\n",ans);
101 }
102 int main()
103 {
104     scanf("%s%s",a+1,b+1);
105     n=strlen(a+1);m=strlen(b+1);
106     last=root=size=1;
107     for(int i=1;i<=m;i++)insert(b[i]-'a');
108     for(int i=1;i<=n;i++)
109     {
110         c=a[i]-'a';
111         for(int j=i-1;j>=pre[c];j--)nexa[j][c]=i;
112         pre[c]=i;
113     }
114     memset(pre,0,sizeof(pre));
115     for(int i=1;i<=m;i++)
116     {
117         c=b[i]-'a';
118         for(int j=i-1;j>=pre[c];j--)nexb[j][c]=i;
119         pre[c]=i;
120     }
121     work1();work2();work3();work4();
122     return 0;
123 }
View Code

4.【bzoj3998】[TJOI2015]弦论

题意:对于一个给定长度为N的字符串,求它的第K小子串是什么。

分析:hzwerの博客

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 #define LL long long
 5 using namespace std;
 6 const int N=5e5+5;
 7 int n,root,last,size,T,K,x;
 8 int c[N<<1],val[N<<1],q[N<<1],sum[N<<1];
 9 char s[N];
10 struct SAM{int mx,fa,ch[26];}t[N<<1];
11 void insert(int c)
12 {
13     int np=++size;val[np]=1;
14     t[np].mx=t[last].mx+1;
15     int x=last;last=np;
16     while(x&&!t[x].ch[c])t[x].ch[c]=np,x=t[x].fa;
17     if(!x)t[np].fa=root;
18     else
19     {
20         int y=t[x].ch[c];
21         if(t[y].mx==t[x].mx+1)t[np].fa=y;
22         else
23         {
24             int nq=++size;
25             t[nq]=t[y];
26             t[nq].mx=t[x].mx+1;
27             t[y].fa=t[np].fa=nq;
28             while(x&&t[x].ch[c]==y)t[x].ch[c]=nq,x=t[x].fa;
29         }
30     }
31 }
32 void dfs(int now,int K)
33 {
34     if(K<=val[now])return;
35     K-=val[now];
36     for(int i=0;i<26;i++)
37     {
38         x=t[now].ch[i];
39         if(!x)continue;
40         if(K<=sum[x]){putchar(i+'a');dfs(x,K);return;}
41         K-=sum[x];
42     }
43 }
44 int main()
45 {
46     scanf("%s%d%d",s+1,&T,&K);
47     n=strlen(s+1);root=last=size=1;
48     for(int i=1;i<=n;i++)insert(s[i]-'a');
49     for(int i=1;i<=size;i++)c[t[i].mx]++;
50     for(int i=1;i<=size;i++)c[i]+=c[i-1];
51     for(int i=1;i<=size;i++)q[c[t[i].mx]--]=i;
52     for(int i=size;i>=1;i--)
53     {
54         x=q[i];
55         if(T==1)val[t[x].fa]+=val[x];
56         else val[x]=1;
57     }
58     val[1]=0;
59     for(int i=size;i>=1;i--)
60     {
61         x=q[i];sum[x]=val[x];
62         for(int j=0;j<26;j++)sum[x]+=sum[t[x].ch[j]];
63     }
64     if(K>sum[root]){printf("-1\n");return 0;}
65     dfs(root,K);
66     return 0;
67 }
View Code

5.【bzoj2780】[Spoj]8093 Sevenk Love Oimaster

题意:给你n个文本串,m个询问串,询问每个串在多少个文本串中出现过。

分析:广义后缀自动机。

 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cstring>
 4 #define LL long long
 5 using namespace std;
 6 const int N=1e4+5;
 7 const int M=1e5+5;
 8 int n,m,last,root,size,now,len,tmp;
 9 int L[N],R[N],vis[M<<1],sz[M<<1];
10 char str[M];
11 struct SAM{int fa,mx,ch[26];}t[M<<1];
12 void insert(int c)
13 {
14     int np=++size;
15     t[np].mx=t[last].mx+1;
16     int x=last;last=np;
17     while(x&&!t[x].ch[c])t[x].ch[c]=np,x=t[x].fa;
18     if(!x)t[np].fa=root;
19     else
20     {
21         int y=t[x].ch[c];
22         if(t[y].mx==t[x].mx+1)t[np].fa=y;
23         else
24         {
25             int nq=++size;
26             t[nq]=t[y];
27             t[nq].mx=t[x].mx+1;
28             t[y].fa=t[np].fa=nq;
29             while(x&&t[x].ch[c]==y)t[x].ch[c]=nq,x=t[x].fa;
30         }
31     }
32 }
33 void up(int x,int id)
34 {
35     while(x&&vis[x]!=id)
36     {
37         sz[x]++;vis[x]=id;
38         x=t[x].fa;
39     }
40 }
41 int main()
42 {
43     last=root=size=1;
44     scanf("%d%d",&n,&m);
45     for(int i=1;i<=n;i++)
46     {
47         L[i]=R[i-1];scanf("%s",str+L[i]);
48         R[i]=strlen(str);last=1;
49         for(int j=L[i];j<R[i];j++)insert(str[j]-'a');
50     }
51     for(int i=1;i<=n;i++)
52     {
53         now=1;
54         for(int j=L[i];j<R[i];j++)
55             now=t[now].ch[str[j]-'a'],up(now,i);
56     }
57     int i;
58     while(m--)
59     {
60         scanf("%s",str);
61         len=strlen(str);now=1;
62         for(i=0;i<len;i++)
63         {
64             tmp=t[now].ch[str[i]-'a'];
65             if(!tmp)break;now=tmp;
66         }
67         if(i==len)printf("%d\n",sz[now]);
68         else printf("0\n");
69     }
70     return 0;
71 }
View Code

【序列自动机】

〖模板代码

1 for(int i=1;i<=n;i++)
2 {
3     c=s[i]-'a';
4     for(int j=i-1;j>=pre[c];j--)nex[j][c]=i;
5     pre[c]=i;
6 }
View Code

【Manacher】

〖模板代码

 1 #include<cstdio>
 2 #include<algorithm> 
 3 #include<cstring>
 4 #include<cmath>
 5 #define LL long long
 6 using namespace std;
 7 const int N=2e7+2e6+5;
 8 int n,len,ans,p[N];
 9 char ch[N],s[N];
10 int main()
11 {
12     scanf("%s",ch+1);
13     len=strlen(ch+1);
14     s[0]='!';s[1]='#';n=1;
15     for(int i=1;i<=len;i++)
16         s[++n]=ch[i],s[++n]='#';
17     int mxr=0,id=0;
18     for(int i=1;i<=n;i++)
19     {
20         if(i<mxr)p[i]=min(p[2*id-i],mxr-i);
21         else p[i]=1;
22         for(;i+p[i]<=n&&s[i-p[i]]==s[i+p[i]];p[i]++);
23         if(i+p[i]>mxr)mxr=i+p[i],id=i;
24         ans=max(ans,p[i]-1);
25     }
26     printf("%d",ans);
27     return 0;
28 }
View Code

〖相关题目

1.【bzoj2342】[Shoi2011]双倍回文

题意:见原题

分析:hzwerの博客

 1 #include<algorithm>
 2 #include<cmath>
 3 #include<cstdio>
 4 #include<cstring>
 5 #include<set>
 6 #define LL long long
 7 using namespace std;
 8 const int N=1e6+5;
 9 int len,n,ans,p[N],f[N]; 
10 char ch[N],s[N];
11 set<int>bt;
12 struct node{int x,id;}a[N];
13 bool cmp(node a,node b){return a.x<b.x;}
14 void manacher()
15 {
16     int mxr=0,id=0;
17     for(int i=1;i<=n;i++)
18     {
19         if(i<mxr)p[i]=min(p[2*id-i],mxr-i);else p[i]=1;
20         for(;i+p[i]<=n&&s[i-p[i]]==s[i+p[i]];p[i]++);
21         if(i+p[i]>mxr)mxr=i+p[i],id=i;
22     }
23 }
24 int main()
25 {
26     scanf("%d%s",&len,ch+1);
27     s[0]='!';s[1]='#';n=1;
28     for(int i=1;i<=len;i++)s[++n]=ch[i],s[++n]='#';
29     manacher();
30     for(int i=1;i<=len;i++)f[i]=p[(i<<1)|1]-1,f[i]>>=1;
31     for(int i=1;i<=len;i++)a[i].id=i,a[i].x=i-f[i];
32     sort(a+1,a+len+1,cmp);
33     int id=0;
34     for(int i=1;i<=len;i++)
35     {
36          while(id+1<=n&&a[id+1].x<=i)id++,bt.insert(a[id].id);
37          set<int>::iterator now=bt.upper_bound(i+f[i]/2);
38          if(now!=bt.begin())ans=max(ans,((*--now)-i)*4);
39     }
40     printf("%d\n",ans);
41     return 0;
42 }
View Code

 

转载于:https://www.cnblogs.com/zsnuo/p/8240923.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值