zoukankan      html  css  js  c++  java
  • 字符串(后缀自动机):NOI 2016 优秀的拆分

    【问题描述】

      如果一个字符串可以被拆分为 AABB 的形式,其中 A 和 B 是任意非空字符串, 则我们称该字符串的这种拆分是优秀的。

      例如,对于字符串 aabaabaa,如果令 A = aab, B = a, 我们就找到了这个字符串拆分成 AABB 的一种方式。

      一个字符串可能没有优秀的拆分,也可能存在不止一种优秀的拆分。 比如我们令 A = a, B = baa,也可以用 AABB 表示出上述字符串;但是,字符串abaabaa 就没有优秀的拆分。

      现在给出一个长度为 n 的字符串 S,我们需要求出,在它所有子串的所有拆分方式中,优秀拆分的总个数。 这里的子串是指字符串中连续的一段。

        以下事项需要注意:

        1. 出现在不同位置的相同子串,我们认为是不同的子串,它们的优秀拆分均会被计入答案。

        2. 在一个拆分中,允许出现 A = B。例如 cccc 存在拆分 A = B = c

        3. 字符串本身也是它的一个子串。

    【输入格式】

    每个输入文件包含多组数据。输入文件的第一行只有一个整数 T,表示数据 的组数。保证 1 ≤ T ≤ 10。

    接下来 T 行,每行包含一个仅由英文小写字母构成的字符串 S,意义如题 所述。

    【输出格式】

    输出 T 行,每行包含一个整数,表示字符串 S 所有子串的所有拆分中, 总共有多少个是优秀的拆分。

    【样例输入】

    4
    aabbbb
    cccccc
    aabaabaabaa
    bbaabaababaaba
    

    【样例输出】

    3 5 4 7
    

    【样例说明】

    我们用 S[i, j] 表示字符串 S 第 i 个字符到第 j 个字符的子串(从 1 开 始计数)。

    第一组数据中,共有 3 个子串存在优秀的拆分:

    S[1,4] = aabb,优秀的拆分为 A = a, B = b

    S[3,6] = bbbb,优秀的拆分为 A = b, B = b

    S[1,6] = aabbbb,优秀的拆分为 A = a, B = bb

    而剩下的子串不存在优秀的拆分,所以第一组数据的答案是 3。

    第二组数据中, 有两类,总共 4 个子串存在优秀的拆分:

    对于子串 S[1,4] = S[2,5] = S[3,6] = cccc, 它们优秀的拆分相同,均为 A = c, B = c,但由于这些子串位置不同,因此要计算 3 次;

    对于子串 S[1,6] = cccccc,它优秀的拆分有 2 种: A = c, B = cc 和 A = cc, B = c,它们是相同子串的不同拆分,也都要计入答案。

    所以第二组数据的答案是 3 + 2 = 5。

    第三组数据中, S[1,8] 和 S[4,11] 各有 2 种优秀的拆分,其中 S[1,8] 是 问题描述中的例子,所以答案是 2 + 2 = 4。

    第四组数据中, S[1,4], S[6,11], S[7,12], S[2,11], S[1,8] 各有 1 种优秀 的拆分, S[3,14] 有 2 种优秀的拆分,所以答案是 5 + 2 = 7。

    【更多样例】

    下载

    【样例 2 输入输出】

    见目录下的 excellent/excellent2.in 与 excellent/excellent2.ans。

    【样例 3 输入输出】

    见目录下的 excellent/excellent3.in 与 excellent/excellent3.ans。

    【子任务】

    对于全部的测试点,保证 1 ≤ T ≤ 10。 以下对数据的限制均是对于单组输入数据而言的,也就是说同一个测试点下的 T 组数据均满足限制条件。

    我们假定 n 为字符串 S 的长度,每个测试点的详细数据范围见下表:

    【来源】

    NOI2016 Day1 T1

      这道题有点难想到正解。

      枚举长度i,然后把字符串拆分成许多连续的长度为i的子串,通过比较LCS与LCP得出一段的答案,这里发现答案是区间加法,考虑用线段树很可能超时,这里用的是差分,看程序很好理解。还有一个地方要注意:更新答案时可能会重复计算,只需要确保每次枚举都只在一段限定的区间更新,就不会出现重叠。

      1 #include <iostream>
      2 #include <cstring>
      3 #include <cstdio>
      4 using namespace std;
      5 const int N=120010;
      6 struct SAM{
      7     char s[N];
      8     int fa[N],pos[N],sa[N],rank[N];
      9     int son[N][26],end[N],rht[N],lcp[N];
     10     int ch[N][26],len[N],id[N],tot;
     11     int od[N],wv[N],lst,cnt;
     12     int mm[N],Min[N][25];
     13     void Init(){
     14         memset(s,0,sizeof(s)); 
     15         memset(ch,0,sizeof(ch));
     16         memset(end,0,sizeof(end));
     17         memset(son,0,sizeof(son));
     18         memset(pos,0,sizeof(pos));
     19         lst=cnt=1;tot=0;
     20     }
     21 
     22     void Insert(int c){
     23         int p=lst,np=lst=++cnt;end[lst]=1;
     24         id[len[np]=len[p]+1]=np;rht[np]=1;
     25         while(p&&!ch[p][c])ch[p][c]=np,p=fa[p];
     26         if(!p)fa[np]=1;
     27         else{
     28             int q=ch[p][c],nq;
     29             if(len[q]==len[p]+1)fa[np]=q;
     30             else{
     31                 len[nq=++cnt]=len[p]+1;
     32                 fa[nq]=fa[q];fa[q]=fa[np]=nq;
     33                 memcpy(ch[nq],ch[q],sizeof(ch[q]));
     34                 while(ch[p][c]==q)ch[p][c]=nq,p=fa[p];
     35             }
     36         }
     37     }
     38 
     39     void Get_Right(){
     40         for(int i=1;i<=cnt;i++)wv[len[i]]++;
     41         for(int i=1;i<=cnt;i++)wv[i]+=wv[i-1];
     42         for(int i=1;i<=cnt;i++)od[wv[len[i]]--]=i;
     43         for(int i=cnt;i>=1;i--)rht[fa[od[i]]]+=rht[od[i]];
     44     }
     45 
     46     void Build_Tree(){
     47         int l=strlen(s+1);
     48         for(int i=l;i>=1;i--)Insert(s[i]-'a');
     49         for(int i=l;i>=1;i--)
     50             for(int x=id[i],p=l+1;x&&!pos[x];x=fa[x])
     51                 p-=len[x]-len[fa[x]],pos[x]=p;
     52         for(int x=2;x<=cnt;x++)son[fa[x]][s[pos[x]]-'a']=x;    
     53     }
     54 
     55     void DFS(int x,int l){
     56         if(end[x])sa[rank[l-len[x]+1]=++tot]=l-len[x]+1;
     57         for(int i=0;i<26;i++)if(son[x][i])DFS(son[x][i],l);
     58     }
     59 
     60     void Build_SA(){
     61         int l=strlen(s+1),k=0;DFS(1,l);
     62         for(int i=1,j;i<=l;lcp[rank[i++]]=k)
     63             for(k?k--:k,j=sa[rank[i]-1];s[i+k]==s[j+k];k++);
     64         mm[0]=-1;
     65         for(int i=1;i<=l;i++){
     66             mm[i]=(i&(i-1))?mm[i-1]:mm[i-1]+1;
     67             Min[i][0]=lcp[i];
     68         }
     69         for(int k=1;k<=mm[l];k++)
     70             for(int i=1;i+(1<<k-1)<=l;i++)
     71                 Min[i][k]=min(Min[i][k-1],Min[i+(1<<(k-1))][k-1]);
     72     }
     73     
     74     int LCP(int x,int y){
     75         if(x>y)swap(x,y);x+=1;int k=mm[y-x+1];
     76         int ret=min(Min[x][k],Min[y-(1<<k)+1][k]);
     77         return ret; 
     78     }
     79 }A,B;
     80 
     81 int ln,T,f[N],g[N];char s[N];
     82 int Get_LCP(int x,int y){return A.LCP(A.rank[x],A.rank[y]);}
     83 int Get_LCS(int x,int y){return B.LCP(B.rank[ln-x+1],B.rank[ln-y+1]);} 
     84 
     85 int main(){
     86     freopen("excellent.in","r",stdin);
     87     freopen("excellent.out","w",stdout);
     88     scanf("%d",&T);
     89     while(T--){
     90         A.Init();B.Init();
     91         scanf("%s",s+1);ln=strlen(s+1);
     92         for(int i=1;i<=ln;i++){A.s[i]=s[i];B.s[i]=s[ln-i+1];}
     93         A.Build_Tree();A.Build_SA();
     94         B.Build_Tree();B.Build_SA();
     95         for(int i=1;i<=ln;i++)f[i]=g[i]=0;
     96         
     97         for(int i=1,l,r,x;i+i<=ln;i++)
     98             for(int j=i;(x=j+i)<=ln;j+=i)if(s[j]==s[x]){
     99                 l=x-Get_LCS(j,x)+1;r=x+Get_LCP(j,x)-1;
    100                 l=max(l+i-1,x);r=min(r,x+i-1);
    101                 if(l>r)continue;
    102                 f[l]++,f[r+1]--;
    103                    g[l-i-i+1]++,g[r+1-i-i+1]--;
    104             }
    105         long long ans=0;
    106         for(int i=1;i<=ln;i++)f[i]+=f[i-1],g[i]+=g[i-1];
    107         for(int i=1;i<ln;i++)ans+=f[i]*g[i+1];
    108         printf("%lld
    ",ans);
    109     }
    110     return 0;
    111 }

      

  • 相关阅读:
    零知识证明 SNARKs 第2部分:多项式盲估
    零知识证明 SNARKs 第3部分:知识系数测试和假设
    零知识证明 SNARKs 第4部分:如何进行可验证多项式盲估
    网站渗透测试原理及详细过程
    网站渗透测试原理及详细过程
    网站渗透测试原理及详细过程
    网站渗透测试原理及详细过程
    Java网络编程从入门到精通(27):关闭服务端连接
    Java网络编程从入门到精通(27):关闭服务端连接
    Java网络编程从入门到精通(27):关闭服务端连接
  • 原文地址:https://www.cnblogs.com/TenderRun/p/5761718.html
Copyright © 2011-2022 走看看