zoukankan      html  css  js  c++  java
  • 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
  • 相关阅读:
    code3728 联合权值
    Codevs 4600 [NOI2015]程序自动分析
    code1540 银河英雄传说
    code1074 食物链
    堆排序
    哈夫曼树与哈夫曼码
    优先队列用法
    code1154 能量项链
    code1225 八数码Bfs
    javascript5
  • 原文地址:https://www.cnblogs.com/SilverNebula/p/6789746.html
Copyright © 2011-2022 走看看