HihoCoder 重复旋律

あの旋律を何度も繰り返しでも、あの日見た光景を再現できない

无论将那段旋律重复多少次,也无法重现那一日我们看到的景象

 

もし切ないならば、時をまきもどしてみるかい?

若是感到惆怅的话,要试着让时光倒流吗?

 

NONONOーー

 

今が最高!

以上部分请无视

hihocoder的一套后缀数组/后缀自动机模板(并不)题

敲了⑨遍后缀板子,也是带感

没有计时,不知道和隔壁某859′′比怎么样 2333

  

后缀数组一·重复旋律

时间限制:5000ms  单点时限:1000ms  内存限制:256MB

描述

小Hi平时的一大兴趣爱好就是演奏钢琴。我们知道一个音乐旋律被表示为长度为 N 的数构成的数列。

小Hi在练习过很多曲子以后发现很多作品自身包含一样的旋律。旋律是一段连续的数列,相似的旋律在原数列可重叠。比如在1 2 3 2 3 2 1 中 2 3 2 出现了两次。

小Hi想知道一段旋律中出现次数至少为K次的旋律最长是多少?

输入

第一行两个整数 N和K。1≤N≤20000 1≤K≤N

接下来有 N 个整数,表示每个音的数字。1≤数字≤100

输出

一行一个整数,表示答案。

后缀数组模板题咯。

二分答案mid,扫描height数组,看大于mid的height有没有连续出现K-1次

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<queue>
 6 using namespace std;
 7 const int mxn=100010;
 8 int read(){
 9     int x=0,f=1;char ch=getchar();
10     while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
11     while(ch>='0' && ch<='9'){x=x*10-'0'+ch;ch=getchar();}
12     return x*f;
13 }
14 int sa[mxn],rk[mxn],ht[mxn],r[mxn];
15 int wa[mxn],wb[mxn],wv[mxn],cnt[mxn];
16 inline int cmp(int *r,int a,int b,int l){
17     return r[a]==r[b] && r[a+l]==r[b+l];
18 }
19 void GetSA(int *sa,int n,int m){
20     int *x=wa,*y=wb;
21     int i,j;
22 //    for(i=0;i<m;i++)cnt[i]=0;
23     for(i=0;i<n;i++)++cnt[x[i]=r[i]];
24     for(i=1;i<m;i++)cnt[i]+=cnt[i-1];
25     for(i=n-1;i>=0;i--)sa[--cnt[x[i]]]=i;
26     for(int j=1,p=0;p<n;j<<=1,m=p){
27         for(p=0,i=n-j;i<n;i++)y[p++]=i;
28         for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
29         for(i=0;i<n;i++)
30             wv[i]=x[y[i]];
31         for(i=0;i<m;i++)cnt[i]=0;
32         for(i=0;i<n;i++)++cnt[wv[i]];
33         for(i=1;i<m;i++)cnt[i]+=cnt[i-1];
34         for(i=n-1;i>=0;i--)sa[--cnt[wv[i]]]=y[i];
35         swap(x,y);
36         p=1;x[sa[0]]=0;
37         for(i=1;i<n;i++)
38             x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++;
39     }
40 /*    printf("SA:
");
41     for(i=0;i<n;i++){
42         printf("%d ",sa[i]);
43     }
44     puts("");*/
45     return;
46 }
47 void GetHt(int n){
48     for(int i=1;i<=n;i++)rk[sa[i]]=i;
49     int k=0;
50     for(int i=0,j;i<n;i++){
51         j=sa[rk[i]-1];
52         if(k)k--;
53         while(r[i+k]==r[j+k])k++;
54         ht[rk[i]]=k;
55     }
56     return;
57 }
58 int n,K;
59 bool calc(int lim){
60     int tmp=1;
61     for(int i=1;i<n;i++){
62         if(ht[i]>=lim)tmp++;
63         else tmp=1;
64         if(tmp>=K)return 1;
65     }
66     return 0;
67 }
68 int main(){
69     int i,j;
70     n=read();K=read();
71     for(i=0;i<n;i++)r[i]=read();
72     GetSA(sa,n+1,105);
73     GetHt(n);
74     int l=0,r=n,ans=0;
75     while(l<=r){
76         int mid=(l+r)>>1;
77         if(calc(mid)){
78             l=mid+1;
79             ans=mid;
80         }
81         else r=mid-1;
82     }
83     printf("%d
",ans);
84     return 0;
85 }
重复旋律1

后缀数组二·重复旋律2

时间限制:5000ms  单点时限:1000ms  内存限制:256MB

描述

小Hi平时的一大兴趣爱好就是演奏钢琴。我们知道一个音乐旋律被表示为长度为 N 的数构成的数列。小Hi在练习过很多曲子以后发现很多作品自身包含一样的旋律。

旋律可以表示为一段连续的数列,相似的旋律在原数列不可重叠,比如在1 2 3 2 3 2 1 中 2 3 2 出现了一次,2 3 出现了两次,小Hi想知道一段旋律中出现次数至少为两次的旋律最长是多少?

输入

第一行一个整数 N。1≤N≤100000

接下来有 N 个整数,表示每个音的数字。1≤数字≤1000

输出

一行一个整数,表示答案。

后缀数组

依旧是二分长度mid,然后看连续的一段大于等于mid的height中,sa[]最大和最小的相隔有没有超过mid

研究了一下,发现如果min和max要卡常的话,在括号内计算不多的情况下define的效果比inline函数好

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<queue>
 6 using namespace std;
 7 const int mxn=100010;
 8 int read(){
 9     int x=0,f=1;char ch=getchar();
10     while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
11     while(ch>='0' && ch<='9'){x=x*10-'0'+ch;ch=getchar();}
12     return x*f;
13 }
14 int sa[mxn],rk[mxn],ht[mxn],r[mxn];
15 int wa[mxn],wb[mxn],cnt[mxn];
16 inline int cmp(int *r,int a,int b,int l){
17     return r[a]==r[b] && r[a+l]==r[b+l];
18 }
19 void GetSA(int *sa,int n,int m){
20     int i,j,p;
21     int *x=wa,*y=wb;
22 //    for(i=1;i<=m;i++)cnt[i]=0;
23     for(i=1;i<=n;i++)++cnt[x[i]=r[i]];
24     for(i=1;i<=m;i++)cnt[i]+=cnt[i-1];
25     for(i=n;i;i--)sa[cnt[x[i]]--]=i;
26     for(j=1,p=0;p<n;j<<=1,m=p){
27         for(p=0,i=n-j+1;i<=n;i++)y[++p]=i;
28         for(i=1;i<=n;i++)if(sa[i]>j)y[++p]=sa[i]-j;
29         for(i=1;i<=m;i++)cnt[i]=0;
30         for(i=1;i<=n;i++)++cnt[x[y[i]]];
31         for(i=1;i<=m;i++)cnt[i]+=cnt[i-1];
32         for(i=n;i;i--)sa[cnt[x[y[i]]]--]=y[i];
33         swap(x,y);
34         p=1;x[sa[1]]=1;
35         for(i=2;i<=n;i++)
36             x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p:++p;
37     }
38     return;
39 }
40 void GetHT(int n){
41     int i,j,k=0;
42     for(i=1;i<=n;i++)rk[sa[i]]=i;
43     for(i=1;i<=n;i++){
44         if(rk[i]==1)continue;
45         if(k)--k;
46         j=sa[rk[i]-1];
47         while(r[i+k]==r[j+k])k++;
48         ht[rk[i]]=k;
49     }
50     return;
51 }
52 #define amin(a,b) ((a)<(b)?(a):(b))
53 #define amax(a,b) ((a)>(b)?(a):(b))
54 int n;
55 bool solve(int lim){
56     int mx=sa[1],mn=sa[1];
57     for(int i=1;i<=n;i++){
58         if(ht[i]>=lim){
59             mx=amax(sa[i],mx);
60             mn=amin(sa[i],mn);
61             if(mx-mn>=lim)return 1;
62         }
63         else mx=mn=sa[i];
64     }
65     return 0;
66 }
67 int main(){
68     int i,j;
69     n=read();
70     for(i=1;i<=n;i++)r[i]=read();
71     GetSA(sa,n,1001);
72     GetHT(n);
73     int l=0,r=n,res=0;
74     while(l<=r){
75         int mid=(l+r)>>1;
76         if(solve(mid)){
77             res=mid;
78             l=mid+1;
79         }else r=mid-1;
80     }
81     printf("%d
",res);
82     return 0;
83 }
重复旋律2

后缀数组三·重复旋律3

时间限制:5000ms
单点时限:1000ms
内存限制:256MB

描述

小Hi平时的一大兴趣爱好就是演奏钢琴。我们知道一个音乐旋律被表示为长度为 N 的数构成的数列。小Hi在练习过很多曲子以后发现很多作品中的旋律有共同的部分。

旋律是一段连续的数列,如果同一段旋律在作品A和作品B中同时出现过,这段旋律就是A和B共同的部分,比如在abab 在 bababab 和 cabacababc 中都出现过。小Hi想知道两部作品的共同旋律最长是多少?

输入

共两行。一行一个仅包含小写字母的字符串。字符串长度不超过 100000。

输出

一行一个整数,表示答案。

后缀数组模板题

LCP嘛

把两个串连起来,中间用特殊符号隔开

扫一遍height数组,ans=max(ans,height[i]) (sa[i]和sa[i-1]一个在前一个串里,一个在后一个串里)

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<queue>
 6 using namespace std;
 7 const int mxn=200010;
 8 int read(){
 9     int x=0,f=1;char ch=getchar();
10     while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
11     while(ch>='0' && ch<='9'){x=x*10-'0'+ch;ch=getchar();}
12     return x*f;
13 }
14 int sa[mxn],rk[mxn],ht[mxn],r[mxn];
15 int wa[mxn],wb[mxn],cnt[mxn];
16 inline int cmp(int *r,int a,int b,int l){
17     return r[a]==r[b] && r[a+l]==r[b+l];
18 }
19 void GetSA(int *sa,int n,int m){
20     int *x=wa,*y=wb;
21     int i,j,p;
22 //    for(i=0;i<m;i++)cnt[i]=0;
23     for(i=0;i<n;i++)cnt[x[i]=r[i]]++;
24     for(i=1;i<m;i++)cnt[i]+=cnt[i-1];
25     for(i=n-1;i>=0;i--)sa[--cnt[x[i]]]=i;
26     for(j=1,p=0;p<n;j<<=1,m=p){
27         p=0;for(i=n-j;i<n;i++)y[p++]=i;
28         for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
29         for(i=0;i<m;i++)cnt[i]=0;
30         for(i=0;i<n;i++)++cnt[x[y[i]]];
31         for(i=1;i<m;i++)cnt[i]+=cnt[i-1];
32         for(i=n-1;i>=0;i--)sa[--cnt[x[y[i]]]]=y[i];
33         swap(x,y);
34         p=1;x[sa[0]]=0;
35         for(i=1;i<n;i++)
36             x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++;
37     }
38     return;
39 }
40 void GetHT(int n){
41     int i,j,k=0;
42     for(i=1;i<=n;i++)rk[sa[i]]=i;
43     for(i=0;i<n;i++){
44         if(k)k--;
45         j=sa[rk[i]-1];
46         while(r[i+k]==r[j+k])k++;
47         ht[rk[i]]=k;
48     }
49     return;
50 }
51 void solve(int n,int ed){
52     int res=0;
53     for(int i=1;i<=ed;i++){
54         if((sa[i]<=n && sa[i-1]>n)||(sa[i]>n && sa[i-1]<=n))
55             res=max(res,ht[i]);
56     }
57     printf("%d
",res);
58     return;
59 }
60 char s[mxn];
61 int main(){
62     int i,j;
63     scanf("%s",s);
64     int len=strlen(s);
65     for(i=0;i<len;i++)r[i]=s[i]-'a'+1;
66     r[len]=27;
67     int ed=len;
68     scanf("%s",s);
69     len=strlen(s);
70     for(i=0;i<len;i++)r[ed+i]=s[i]-'a'+1;
71     len+=ed;
72     GetSA(sa,len+1,28);
73     GetHT(len);
74     solve(ed,len);
75     return 0;
76 }
重复旋律3

后缀数组四·重复旋律4

时间限制:5000ms  单点时限:1000ms  内存限制:256MB

描述

小Hi平时的一大兴趣爱好就是演奏钢琴。我们知道一个音乐旋律被表示为长度为 N 的数构成的数列。小Hi在练习过很多曲子以后发现很多作品中的旋律有重复的部分。

我们把一段旋律称为(k,l)-重复的,如果它满足由一个长度为l的字符串重复了k次组成。 如旋律abaabaabaaba是(4,3)重复的,因为它由aba重复4次组成。

小Hi想知道一部作品中k最大的(k,l)-重复旋律。

输入

一行一个仅包含小写字母的字符串。字符串长度不超过 100000。

输出

一行一个整数,表示答案k。

后缀数组 ST表

这题有点带感。

枚举循环节的长度,然后枚举循环节的整数倍位置作为起点,算一下每一段之间的LCP,再花式处理一下边角情况即可。

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<queue>
 6 using namespace std;
 7 const int mxn=100010;
 8 int sa[mxn],rk[mxn],ht[mxn],r[mxn];
 9 int wa[mxn],wb[mxn],cnt[mxn];
10 inline int cmp(int *r,int a,int b,int l){return r[a]==r[b] && r[a+l]==r[b+l];}
11 void GetSA(int *sa,int n,int m){
12     int *x=wa,*y=wb;
13     int i,j,p;
14 //    for(i=0;i<m;i++)cnt[i]=0;
15     for(i=0;i<n;i++)cnt[x[i]=r[i]]++;
16     for(i=1;i<m;i++)cnt[i]+=cnt[i-1];
17     for(i=n-1;i>=0;i--)sa[--cnt[x[i]]]=i;
18     for(j=1,p=0;p<n;j<<=1,m=p){
19         p=0;for(i=n-j;i<n;i++)y[p++]=i;
20         for(i=0;i<n;i++)if(sa[i]>=j)y[p++]=sa[i]-j;
21         for(i=0;i<m;i++)cnt[i]=0;
22         for(i=0;i<n;i++)++cnt[x[y[i]]];
23         for(i=1;i<m;i++)cnt[i]+=cnt[i-1];
24         for(i=n-1;i>=0;i--)sa[--cnt[x[y[i]]]]=y[i];
25         swap(x,y);
26         p=1;x[sa[0]]=0;
27         for(i=1;i<n;i++)
28             x[sa[i]]=cmp(y,sa[i],sa[i-1],j)?p-1:p++;
29     }
30     return;
31 }
32 void GetHT(int n){
33     int i,j,k=0;
34     for(i=1;i<=n;i++)rk[sa[i]]=i;
35     for(i=0;i<n;i++){
36         if(k)k--;
37         j=sa[rk[i]-1];
38         while(r[i+k]==r[j+k])k++;
39         ht[rk[i]]=k;
40     }
41     return;
42 }
43 int st[mxn][18],lg[mxn];
44 int n;
45 void ST_init(){
46     int i,j;lg[0]=-1;
47     for(i=1;i<=n;i++){
48         lg[i]=lg[i>>1]+1;
49         st[i][0]=ht[i];
50     }
51     for(j=1;j<18;j++)
52         for(i=1;i<=n && i+(1<<j)<=n;i++)
53             st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
54     return;
55 }
56 int ST(int x,int y){
57     if(x>y)swap(x,y); ++x;
58 //    printf("%d %d ",x,y);
59     int tmp=lg[y-x+1];
60     return min(st[x][tmp],st[y-(1<<tmp)+1][tmp]);
61 }
62 char s[mxn];
63 int main(){
64     int i,j;
65     scanf("%s",s);
66     n=strlen(s);
67     for(i=0;i<n;i++)r[i]=s[i]-'a'+1;
68     GetSA(sa,n+1,27);
69     GetHT(n);
70     ST_init();
71     int ans=0;
72     for(i=1;i<=n;i++){//枚举间隔长度 
73         for(j=0;j+i<n;j+=i){
74             int x=ST(rk[j],rk[j+i]);
75             ans=max(ans,x/i+1);
76             if(j-i+x%i>0){
77                 x=ST(rk[j-i+x%i],rk[j+x%i]);
78                 ans=max(ans,x/i+1);
79             }
80         }
81     }
82     printf("%d
",ans);
83     return 0;
84 }
重复旋律4

后缀自动机二·重复旋律5

时间限制:10000ms
单点时限:2000ms
内存限制:512MB

描述

小Hi平时的一大兴趣爱好就是演奏钢琴。我们知道一个音乐旋律被表示为一段数构成的数列。

现在小Hi想知道一部作品中出现了多少不同的旋律?

输入

共一行,包含一个由小写字母构成的字符串。字符串长度不超过 1000000。

输出

一行一个整数,表示答案。

后缀自动机模板题

忘了开longlong WA了一串,尴尬

 1 /*by SilverN*/
 2 #include<iostream>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cstdio>
 6 #include<cmath>
 7 #include<vector>
 8 using namespace std;
 9 const int mxn=2000010;
10 int read(){
11     int x=0,f=1;char ch=getchar();
12     while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
13     while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
14     return x*f;
15 }
16 struct SAM{
17     int t[mxn][26],fa[mxn],mx[mxn];
18     int w[mxn],rk[mxn];
19     int S,cnt,last;
20     void init(){S=cnt=last=1;return;}
21     void insert(int c){
22         int np=++cnt,p=last;last=np;
23         mx[np]=mx[p]+1;
24         for(;p && !t[p][c];p=fa[p])t[p][c]=np;
25         if(!p){fa[np]=S;}
26         else{
27             int q=t[p][c];
28             if(mx[q]==mx[p]+1)fa[np]=q;
29             else{
30                 int nq=++cnt;
31                 mx[nq]=mx[p]+1;
32                 memcpy(t[nq],t[q],sizeof t[q]);
33                 fa[nq]=fa[q];
34                 fa[q]=fa[np]=nq;
35                 for(;p && t[p][c]==q;p=fa[p])t[p][c]=nq;
36             }
37         }
38         return;
39     }
40     void solve(int n){
41         for(int i=1;i<=cnt;i++)w[mx[i]]++;
42         for(int i=1;i<=n;i++)w[i]+=w[i-1];
43         for(int i=cnt;i;i--)rk[w[mx[i]]--]=i;
44         long long ans=0;
45         for(int i=cnt;i;i--){
46             int tmp=rk[i];
47             ans+=mx[tmp]-mx[fa[tmp]];
48         }
49         printf("%lld
",ans);
50         return;
51     }
52 }sa;
53 char s[mxn];
54 int n,m;
55 int main(){
56     int i,j;
57     scanf("%s",s+1);
58     n=strlen(s+1);
59     sa.init();
60     for(i=1;i<=n;i++)
61         sa.insert(s[i]-'a');
62     sa.solve(n);
63     return 0;
64 }
重复旋律5

后缀自动机三·重复旋律6

时间限制:15000ms  单点时限:3000ms  内存限制:512MB

描述

小Hi平时的一大兴趣爱好就是演奏钢琴。我们知道一个音乐旋律被表示为一段数构成的数列。

现在小Hi想知道一部作品中所有长度为K的旋律中出现次数最多的旋律的出现次数。但是K不是固定的,小Hi想知道对于所有的K的答案。

输入

共一行,包含一个由小写字母构成的字符串S。字符串长度不超过 1000000。

输出

共Length(S)行,每行一个整数,表示答案。

后缀自动机 DP

倒着更新答案

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstdio>
 4 #include<cstring>
 5 using namespace std;
 6 const int mxn=2000010;
 7 int read(){
 8     int x=0,f=1;char ch=getchar();
 9     while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
10     while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
11     return x*f;
12 }
13 void write(int x){
14     if(x<0)putchar('-'),x=-x;
15     if(x>9)write(x/10);
16     putchar(x%10+'0');
17     return;
18 }
19 int f[mxn];
20 struct SAM{
21     int t[mxn][26],fa[mxn],mx[mxn],sz[mxn];
22     int w[mxn],rk[mxn];
23     int S,cnt,last;
24     void init(){
25         S=cnt=last=1;return;
26     }
27     void insert(int c){
28         int np=++cnt,p=last;last=np;
29         mx[np]=mx[p]+1;
30         for(;p && !t[p][c];p=fa[p])t[p][c]=np;
31         if(!p)fa[np]=S;
32         else{
33             int q=t[p][c];
34             if(mx[q]==mx[p]+1)fa[np]=q;
35             else{
36                 int nq=++cnt;mx[nq]=mx[p]+1;
37                 memcpy(t[nq],t[q],sizeof t[q]);
38                 fa[nq]=fa[q];
39                 fa[q]=fa[np]=nq;
40                 for(;p && t[p][c]==q;p=fa[p])t[p][c]=nq;
41             }
42         }
43         sz[np]=1;
44         return;
45     }
46     void solve(int n){
47         for(int i=1;i<=cnt;i++)++w[mx[i]];
48         for(int i=1;i<=n;i++)w[i]+=w[i-1];
49         for(int i=cnt;i;i--)rk[w[mx[i]]--]=i;
50         for(int i=cnt;i;i--){
51             int tmp=rk[i];
52             sz[fa[tmp]]+=sz[tmp];
53             f[mx[tmp]]=max(f[mx[tmp]],sz[tmp]);
54         }
55         for(int i=n-1;i;i--)f[i]=max(f[i],f[i+1]);
56         for(int i=1;i<=n;i++){
57             write(f[i]);puts("");
58         }
59         return;
60     }
61 }sa;
62 char s[mxn];
63 int main(){
64 //    freopen("in.txt","r",stdin);
65     int i,j;
66     scanf("%s",s+1);
67     int n=strlen(s+1);
68     sa.init();
69     for(i=1;i<=n;i++)sa.insert(s[i]-'a');
70     sa.solve(n);
71     return 0;
72 }
重复旋律6

后缀自动机四·重复旋律7

时间限制:15000ms  单点时限:3000ms  内存限制:512MB

描述

小Hi平时的一大兴趣爱好就是演奏钢琴。我们知道一段音乐旋律可以被表示为一段数构成的数列。

神奇的是小Hi发现了一部名字叫《十进制进行曲大全》的作品集,顾名思义,这部作品集里有许多作品,但是所有的作品有一个共同特征:只用了十个音符,所有的音符都表示成0-9的数字。

现在小Hi想知道这部作品中所有不同的旋律的“和”(也就是把串看成数字,在十进制下的求和,允许有前导0)。答案有可能很大,我们需要对(10^9 + 7)取摸。

输入

第一行,一个整数N,表示有N部作品。

接下来N行,每行包含一个由数字0-9构成的字符串S。

所有字符串长度和不超过 1000000。

输出

共一行,一个整数,表示答案 mod (10^9 + 7)。

后缀自动机 DFS DP

这题挺带感的。

倒着把字符串建成后缀自动机,统计出每个状态结点出现的次数后,从root开始记忆化搜索(DP?)每种转移方式的贡献

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<cstdio>
 5 #include<cmath>
 6 #define LL long long
 7 using namespace std;
 8 const int mod=1e9+7;
 9 const int mxn=2000010;
10 LL f[mxn];
11 struct SAM{
12     int t[mxn][10],fa[mxn],mx[mxn];
13     int sz[mxn];
14     int S,cnt,last;
15     void init(){S=cnt=last=1;return;}
16     void insert(int c){
17         int np=++cnt,p=last;last=np;
18         mx[np]=mx[p]+1;
19         for(;p && !t[p][c];p=fa[p])t[p][c]=np;
20         if(!p)fa[np]=S;
21         else{
22             int q=t[p][c];
23             if(mx[q]==mx[p]+1)fa[np]=q;
24             else{
25                 int nq=++cnt;
26                 mx[nq]=mx[p]+1;
27                 memcpy(t[nq],t[q],sizeof t[q]);
28                 fa[nq]=fa[q];
29                 fa[q]=fa[np]=nq;
30                 for(;p && t[p][c]==q;p=fa[p])t[p][c]=nq;
31             }
32         }
33         return;
34     }
35     void solve(int u){
36         if(f[u]!=-1)return;
37         f[u]=sz[u]=0;
38         for(int i=0;i<10;i++){
39             if(t[u][i])solve(t[u][i]);
40             else continue;
41             int v=t[u][i];
42 //            printf("u:%d v:%d %lld %d
",u,v,f[u],f[v]);
43             (f[u]+=f[v]*10%mod+(LL)i*(sz[v]+1))%=mod;
44             sz[u]+=sz[v]+1;
45             if(sz[u]>=mod)sz[u]-=mod;
46         }
47         return;
48     }
49 }sa;
50 int n;
51 char s[mxn];
52 int main(){
53     int i,j;
54     scanf("%d",&n);
55     sa.init();
56     for(i=1;i<=n;i++){
57         scanf("%s",s+1);
58         int len=strlen(s+1);
59         sa.last=sa.S;
60         for(j=len;j;j--)
61             sa.insert(s[j]-'0');
62     }
63     memset(f,-1,sizeof f);
64     sa.solve(sa.S);
65     printf("%lld
",f[1]);
66     return 0;
67 }
重复旋律7

后缀自动机五·重复旋律8

时间限制:10000ms  单点时限:1000ms  内存限制:256MB

描述

小Hi平时的一大兴趣爱好就是演奏钢琴。我们知道一段音乐旋律可以被表示为一段数构成的数列。

小Hi发现旋律可以循环,每次把一段旋律里面最前面一个音换到最后面就成为了原旋律的“循环相似旋律”,还可以对“循环相似旋律”进行相同的变换能继续得到原串的“循环相似旋律”。

小Hi对此产生了浓厚的兴趣,他有若干段旋律,和一部音乐作品。对于每一段旋律,他想知道有多少在音乐作品中的子串(重复便多次计)和该旋律是“循环相似旋律”。

输入

第一行,一个由小写字母构成的字符串S,表示一部音乐作品。字符串S长度不超过100000。

第二行,一个整数N,表示有N段旋律。接下来N行,每行包含一个由小写字母构成的字符串str,表示一段旋律。所有旋律的长度和不超过 100000。

输出

输出共N行,每行一个整数,表示答案。

后缀自动机 脑洞题

妙啊。把str串的[1~len-1]部分复制一遍接到原串后面,比如abcde就处理成abcdeabcd,在后缀自动机上跑一遍就可以匹配所有循环同构串了。

只有匹配长度大于原str串长的状态能计入答案,统计答案的时候要用vis记录已经统计过的结点,防止重复。

有些实现细节不太理解,代码基本靠抄

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<queue>
 6 using namespace std;
 7 const int mxn=200010;
 8 char s[mxn],a[mxn];
 9 int vis[mxn],dtime=0;
10 struct SAM{
11     int t[mxn][26],fa[mxn],mx[mxn];
12     int w[mxn],rk[mxn],sz[mxn];
13     int S,cnt,last;
14     void init(){S=cnt=last=1;return;}
15     void insert(int c){
16         int np=++cnt,p=last;last=np;
17         mx[np]=mx[p]+1;
18         for(;p && !t[p][c];p=fa[p])t[p][c]=np;
19         if(!p)fa[np]=S;
20         else{
21             int q=t[p][c];
22             if(mx[q]==mx[p]+1)fa[np]=q;
23             else{
24                 int nq=++cnt;mx[nq]=mx[p]+1;
25                 memcpy(t[nq],t[q],sizeof t[q]);
26                 fa[nq]=fa[q];
27                 fa[q]=fa[np]=nq;
28                 for(;p && t[p][c]==q;p=fa[p])t[p][c]=nq;
29             }
30         }
31         sz[np]=1;
32         return;
33     }
34     void ST(int n){
35         int i;
36         for(i=1;i<=cnt;i++)w[mx[i]]++;
37         for(i=1;i<=n;i++)w[i]+=w[i-1];
38         for(i=cnt;i;i--)rk[w[mx[i]]--]=i;//cnt错打成n WA1 
39         for(i=cnt;i;i--){
40             int tmp=rk[i];
41             sz[fa[tmp]]+=sz[tmp];
42         }
43         return;
44     }
45     #define amin(a,b) ((a)<(b)?(a):(b))
46     void solve(int m){
47         int now=S,tmp=0,res=0;
48         int ed=m+m-1;
49         for(int i=1,c;i<=ed;i++){
50             tmp++;
51             c=a[i]-'a';
52             while(now>1 && !t[now][c])now=fa[now];
53             tmp=amin(tmp,mx[now]+1);
54             if(t[now][c])now=t[now][c];
55             while(mx[fa[now]]>=m)now=fa[now];
56             tmp=amin(tmp,mx[now]);
57             if(tmp>=m){
58                 if(vis[now]!=dtime){
59                     vis[now]=dtime;
60                     res+=sz[now];
61                 }
62             }
63         }
64         printf("%d
",res);
65         return;
66     }
67 }sa;
68 int n;
69 int main(){
70     int i,j;
71     scanf("%s",s+1);
72     sa.init();
73     int len=strlen(s+1);
74     for(i=1;i<=len;i++)sa.insert(s[i]-'a');
75     sa.ST(len);
76     scanf("%d",&n);
77     while(n--){
78         scanf("%s",a+1);
79         int m=strlen(a+1);
80         for(i=1;i<m;i++)a[m+i]=a[i];
81         ++dtime;
82         sa.solve(m);
83     }
84     return 0;
85 }
重复旋律8

后缀自动机六·重复旋律9

时间限制:10000ms  单点时限:2000ms  内存限制:256MB

描述

小Hi平时的一大兴趣爱好就是演奏钢琴。我们知道一段音乐旋律可以被表示为一段字符构成的字符串。

现在小Hi已经不满足于单单演奏了!他通过向一位造诣很高的前辈请教,通过几周时间学习了创作钢琴曲的基本理论,并开始对曲目做改编或者原创。两个月后,小Hi决定和前辈进行一场创作曲目的较量!

规则是这样的,有两部已知经典的作品,我们称之为A和B。经典之所以成为经典,必有其经典之处。

刚开始,纸上会有一段A的旋律和一段B的旋律。两人较量的方式是轮流操作,每次操作可以选择在纸上其中一段旋律的末尾添加一个音符,并且要求添加完后的旋律依然是所在作品的旋律(也就是A或B的一个子串)。谁词穷了(无法进行操作)就输了。

小Hi和前辈都足够聪明,但是小Hi还是太年轻,前辈打算教训他一顿。前辈表示会和小Hi进行K次较量,只要小Hi赢了哪怕一次就算小Hi获得最终胜利。但是前提是开始纸上的两段旋律需要他定。小Hi欣然同意,并且表示每次较量都让前辈先操作。

前辈老谋深算,显然是有备而来。他已经洞悉了所有先手必胜的初始(两段)旋律。第i天前辈会挑选字典序第i小的初始(两段)旋律来和小Hi较量。那么问题来了,作为吃瓜群众的你想知道,最后一天即第K天,前辈会定哪两个旋律呢?

初始时两段旋律的字典序比较方式是先比较前一个旋律字典序,一样大则比较后一旋律的字典序。

输入

第一行包含一个正整数,K。K<=10^18

第二行包含一个非空的仅有小写字母构成的字符串,表示作品A。|A|<=10^5

第三行包含一个非空的仅有小写字母构成的字符串,表示作品B。|B|<=10^5

输出

输出共两行,每行一个字符串(可能为空),表示答案。

如果无解则只输出一行"NO"。

后缀自动机 博弈论 sg函数 码农题?

可能算得上是码农题……想明白原理以后就只剩下背板了233

好像有道“讲故事”也是这个套路。

对于单个自动机,可以用sg函数的基本定义(mex)算出每个结点的sg值。

多局面sg游戏,sg异或和不为0的时候先手必胜。

为了优先确保A部分的字典序最小,先在A串的SAM上计算。设当前结点的sg值为nowsg,若加上B串的SAM上所有sg值不为nowsg的状态后总数大于K,就转到B串的自动机上计算,否则继续在A串上贪心转移使得字典序最小……

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<algorithm>
  4 #include<cstring>
  5 #include<queue>
  6 #define LL long long
  7 using namespace std;
  8 const int mxn=200010;
  9 struct SAM{
 10     int t[mxn][26],fa[mxn],sz[mxn],mx[mxn];
 11     int w[mxn],rk[mxn];
 12     LL smm[mxn],ssg[mxn][27];
 13     int sg[mxn];//
 14     int S,cnt,last;
 15     void init(){S=cnt=last=1;return;}
 16     void insert(int c){
 17         int np=++cnt,p=last;last=np;
 18         mx[np]=mx[p]+1;
 19         for(;p && !t[p][c];p=fa[p])t[p][c]=np;
 20         if(!p)fa[np]=S;
 21         else{
 22             int q=t[p][c];
 23             if(mx[q]==mx[p]+1)fa[np]=q;
 24             else{
 25                 int nq=++cnt;mx[nq]=mx[p]+1;
 26                 memcpy(t[nq],t[q],sizeof t[q]);
 27                 fa[nq]=fa[q];
 28                 fa[q]=fa[np]=nq;
 29                 for(;p && t[p][c]==q;p=fa[p])t[p][c]=nq;
 30             }
 31         }
 32         sz[np]=1;
 33         return;
 34     }
 35     void TST(int n){
 36         int i;
 37         for(i=1;i<=cnt;i++)++w[mx[i]];
 38         for(i=1;i<=n;i++)w[i]+=w[i-1];
 39         for(i=cnt;i;i--)rk[w[mx[i]]--]=i;
 40         for(int i=cnt,c;i;i--){
 41             c=rk[i];
 42             sz[fa[c]]+=sz[c];
 43         }
 44         return;
 45     }
 46     void solve(){
 47         bool mex[27];
 48         for(int i=cnt;i;i--){
 49             int tmp=rk[i];
 50             memset(mex,0,sizeof mex);
 51             for(int j=0;j<26;j++){
 52                 if(!t[tmp][j])continue;
 53                 int v=t[tmp][j];
 54                 mex[sg[v]]=1;
 55                 for(int k=0;k<27;k++)
 56                     ssg[tmp][k]+=ssg[v][k];
 57             }
 58             sg[tmp]=0;
 59             while(mex[sg[tmp]])++sg[tmp];
 60             ++ssg[tmp][sg[tmp]];//以tmp为前缀的状态的sg值之和 
 61             smm[tmp]=0;
 62             for(int j=0;j<27;j++)
 63                 smm[tmp]+=ssg[tmp][j];
 64         }
 65         return;
 66     }
 67 }sa,sb;
 68 char s[mxn],c[mxn];
 69 LL K;
 70 void calc(){
 71     int now=sa.S;
 72     LL num=0;
 73     while(1){//优先处理a 
 74         bool flag=1;
 75         int sgnow=sa.sg[now];
 76 //        printf("now:%d sgnow:%d num:%lld cntb:%lld
",
 77 //            now,sgnow,num,sb.smm[1]-sb.ssg[1][sgnow]);
 78         if(num+sb.smm[1]-sb.ssg[1][sgnow]>=K)break;
 79         //如果此时添加b串可以够K个,就不再处理A 
 80         num+=sb.smm[1]-sb.ssg[1][sgnow];
 81         for(int i=0;i<26;i++){
 82             if(!sa.t[now][i])continue;
 83             int v=sa.t[now][i];
 84             LL tmp=0;
 85             for(int j=0;j<27;j++)
 86                 tmp+=(LL)sa.ssg[v][j]*(sb.smm[1]-sb.ssg[1][j]);
 87             if(num+tmp<K)num+=tmp;
 88             else{
 89                 now=v;
 90                 flag=0;
 91                 printf("%c",'a'+i);
 92                 break;
 93             }
 94         }
 95         if(flag){printf("NO
");return;}//26个字母都凑不够数,无解 
 96     }
 97     printf("
");
 98     int sgA=sa.sg[now];
 99     now=sb.S;
100     while(num<K){
101         if(sb.sg[now]!=sgA)num++;
102         if(num==K)break;
103         for(int i=0;i<26;i++){
104             if(!sb.t[now][i])continue;
105             int v=sb.t[now][i];
106             LL tmp=sb.smm[v]-sb.ssg[v][sgA];
107             if(num+tmp<K)num+=tmp;
108             else{
109                 now=v;
110                 printf("%c",'a'+i);
111                 break;
112             }
113         }
114     }
115     return;
116 }
117 int main(){
118     int i,j;
119     cin>>K;
120     scanf("%s",s+1);
121     int len1=strlen(s+1);
122     sa.init();
123     for(i=1;i<=len1;i++)sa.insert(s[i]-'a');
124     sa.TST(len1);
125     sa.solve();
126     //
127     scanf("%s",c+1);
128     int len2=strlen(c+1);
129     sb.init();
130     for(i=1;i<=len2;i++)sb.insert(c[i]-'a');
131     sb.TST(len2);
132     sb.solve();
133     //
134 //    printf("1:%lld
",sb.smm[1]);
135 //    for(i=0;i<27;i++)printf("%lld ",sb.ssg[1][i]);
136 //    puts("");
137     calc();
138     return 0;
139 }
重复旋律9
原文地址:https://www.cnblogs.com/SilverNebula/p/6789746.html