zoukankan      html  css  js  c++  java
  • poj1743 Musical Theme(后缀数组|后缀自动机)

     
    【题目链接】
     
      
     
     
    【题意】
     
     
      求不可重叠最长重复子串。
     

    2015-11-27

    【思路】

      

      1)      据题意处理字符串

      2)      后缀数组。二分长度k,问题成为了判定是否存在两个及以上长度不小于k且互不重叠的子串。根据height数组划分后缀,满足两个条件:一是一组内height值不小于k(保证组内任两个长度不小于k即存在长度不小于k的子串),二是组内后缀sa值的最大最小值之差大于等于k(保证两个子串不重叠)。

      需要注意n==1时需要特判。

      1/为什么可以划分height数组呢?首先height[i]代表lcp(suffix(sa[i]),suffix(sa[i-1])),所以height所对应的后缀是有序的,如果划分出现height<k的话以后的后缀与改组内的lcp一定不大于k-1,所以不会出现后面的再划分到改组的情况。

    【代码】

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 #define FOR(a,b,c) for(int a=(b);a<=(c);a++)
     5 using namespace std;
     6 
     7 const int maxn = 40000+10;
     8 
     9 int s[maxn];
    10 int sa[maxn],t[maxn],t2[maxn],c[maxn],n;
    11 
    12 
    13 void build_sa(int m) {
    14     int i,*x=t,*y=t2;
    15     for(int i=0;i<m;i++) c[i]=0;
    16     for(int i=0;i<n;i++) c[x[i]=s[i]]++;
    17     for(int i=1;i<m;i++) c[i]+=c[i-1];
    18     for(int i=n-1;i>=0;i--) sa[--c[x[i]]]=i;
    19     
    20     for(int k=1;k<=n;k<<=1) {
    21         int p=0;
    22         for(int i=n-k;i<n;i++) y[p++]=i;
    23         for(int i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k;
    24         
    25         for(int i=0;i<m;i++) c[i]=0;
    26         for(int i=0;i<n;i++) c[x[y[i]]]++;
    27         for(int i=0;i<m;i++) c[i]+=c[i-1];
    28         for(int i=n-1;i>=0;i--) sa[--c[x[y[i]]]]=y[i];
    29         
    30         swap(x,y);
    31         p=1;  x[sa[0]]=0;
    32         for(int i=1;i<n;i++)
    33              x[sa[i]]=y[sa[i-1]]==y[sa[i]] && y[sa[i-1]+k]==y[sa[i]+k]?p-1:p++;
    34         if(p>=n)  break;
    35         m=p;
    36     }
    37 }
    38 
    39 int rank[maxn],height[maxn];
    40 void getHeight() {
    41     int i,j,k=0;
    42     for(int i=0;i<n;i++) rank[sa[i]]=i;
    43     for(int i=0;i<n;i++) {
    44         if(k) k--;
    45         int j=sa[rank[i]-1];
    46         while(s[i+k]==s[j+k]) k++;
    47         height[rank[i]]=k;
    48     }
    49 }
    50 
    51 bool can(int k) {
    52     int min=sa[0],max=sa[0];
    53     for(int i=1;i<n;i++) {
    54         if(height[i]<k) min=max=sa[i]; 
    55         if(sa[i]<min) min=sa[i];
    56         if(sa[i]>max) max=sa[i];
    57         if(max-min>=k) return true;
    58     }
    59     return false;
    60 }
    61 
    62 int main() {
    63     while(scanf("%d",&n)==1 && n) 
    64     {
    65         for(int i=0;i<n;i++)scanf("%d",&s[i]);
    66         for(int i=n-1;i>0;i--)s[i]=s[i]-s[i-1]+100;
    67         n--;
    68         for(int i=0;i<n;i++)s[i]=s[i+1];
    69         s[n]=0;        
    70         build_sa(200);
    71         getHeight();
    72         int L=0,R=n/2;
    73         while(L<R) {
    74             int M=L+(R-L+1)/2;
    75             if(can(M)) L=M;  else R=M-1;
    76         }
    77         L++;                      
    78         if(L<=4) printf("0
    ");
    79         else printf("%d
    ",L);
    80     }
    81     return 0;
    82 }
    View Code

     2016-2-19

    【思路】

       

      SAM+DP

      处理出right集的最大值mx和最小值mn,即该状态对应所有字符串的结束位置的最大与最小,递推式为:

        mx[p]=max{ l[p], mx[np],np->fa=p }

        mn[p]=min{ l[p], mn[np],np->fa=p }

      则状态i对应字符串中的最长重复子串的长度为min{l[i],mx[i]-mn[i]},可以拿个栗子自己看一下,这样保证了不重叠。然后取所有状态的最大值即可。

    【代码】

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<iostream>
     4 using namespace std;
     5 
     6 const int N = 4*1e4+10;
     7 const int sigma = 180;
     8 
     9 int s[N/2];
    10 int root,last,sz,ch[N][sigma],fa[N],l[N],mn[N],mx[N];
    11 int b[N],cnt[N],n;
    12 
    13 void init() {
    14     sz=0; root=last=++sz;
    15     memset(fa,0,sizeof(fa));
    16     memset(mx,0,sizeof(mx));
    17     memset(mn,127,sizeof(mn));
    18     memset(cnt,0,sizeof(cnt));
    19     memset(ch,0,sizeof(ch));
    20 }
    21 void add(int x) {
    22     int c=s[x];
    23     int p=last,np=++sz; last=np;
    24     mn[np]=mx[np]=l[np]=x;
    25     for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=np;
    26     if(!p) fa[np]=root;
    27     else {
    28         int q=ch[p][c];
    29         if(l[p]+1==l[q]) fa[np]=q;
    30         else {
    31             int nq=++sz; l[nq]=l[p]+1;
    32             memcpy(ch[nq],ch[q],sizeof(ch[q]));
    33             fa[nq]=fa[q];
    34             fa[np]=fa[q]=nq;
    35             for(;p&&q==ch[p][c];p=fa[p]) ch[p][c]=nq;
    36         }
    37     }
    38 }
    39 void solve() {
    40     for(int i=1;i<=sz;i++) cnt[l[i]]++;
    41     for(int i=1;i<=n;i++) cnt[i]+=cnt[i-1];
    42     for(int i=1;i<=sz;i++) b[cnt[l[i]]--]=i;
    43     int ans=0;
    44     for(int i=sz;i;i--) {
    45         int p=b[i];
    46         if(fa[p]) {
    47             if(mn[fa[p]]>mn[p]) mn[fa[p]]=mn[p];
    48             if(mx[fa[p]]<mx[p]) mx[fa[p]]=mx[p];
    49         }
    50     }
    51     for(int i=1;i<=sz;i++)
    52         ans=max(ans,min(l[i],mx[i]-mn[i]));
    53     if(ans<4) puts("0");
    54     else printf("%d
    ",ans+1);
    55 }
    56 void read(int& x) {
    57     char c=getchar(); int f=1; x=0;
    58     while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    59     while(isdigit(c)) x=x*10+c-'0',c=getchar();
    60     x*=f;
    61 }
    62 
    63 int main() {
    64     while(read(n),n) {
    65         init();
    66         for(int i=1;i<=n;i++) read(s[i]); n--;
    67         for(int i=1;i<=n;i++) s[i]=s[i+1]-s[i]+88,add(i);
    68         solve();
    69     }
    70     return 0;
    71 }
    View Code
  • 相关阅读:
    设置页面标题title
    路由跳转,页面位置不在顶部
    PyTorch深度学习入门
    删除ubuntu后开机进入grub的解决办法
    研一上英语
    python中常用的几个函数的正确用法-lambda/filter/map/reduce
    【Python教程】 re 模块中findall() 函数返回值展现方式的用法详解
    【Python教程】5种常见字符串去除空格的操作方法
    对python中浅拷贝和深拷贝分析详细介绍
    Git使用
  • 原文地址:https://www.cnblogs.com/lidaxin/p/5001802.html
Copyright © 2011-2022 走看看