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
原文地址:https://www.cnblogs.com/lidaxin/p/5001802.html