POJ 1743 [USACO5.1] Musical Theme (后缀数组+二分)

洛谷P2743传送门

题目大意:给你一个序列,求其中最长的一对相似等长子串

一对合法的相似子串被定义为:

1.任意一个子串长度都大于等于5

2.不能有重叠部分

3.其中一个子串可以在全部+/-某个值后和另一个串完全相同

还是老套路了,其实只要求出每一项和前一项的差值序列,这样第三个问题可以被转化为求最长不重叠的相同子串,最后把答案+1就行了

这道题竟然还是LTC大佬的男人八题!正解是后缀数组+二分,但据说可以n^2骗分过

思路是先求出height数组,对于排序后的字符串集合有一个神奇的性质,如果有某连续的几个字符串height>=x,那么这几个字符串的公共前缀长度>=x,可以用Trie树的思想很容易地证明

那现在就要解决第二个问题,不能有重叠部分

直接求解不容易,所以我们二分答案去做,每次二分出一个答案x,然后去height数组里找,把连续的几个height>=x字符串想象成一块,那么如果这个块合法,这一块内一定存在两个字符串的sa[i]-sa[j]>=x,也就是在这一块内找出sa[i]的最大最小值就行啦

复杂度O(nlogn)

傻了吧唧的把后缀数组敲错了,调了半个小时

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #define N 20010
 5 #define inf 0x3f3f3f3f
 6 #define maxn 100
 7 using namespace std;
 8 
 9 int n,len;
10 int a[N],b[N],tr[N],rk[N],sa[N],hs[N],h[N];
11 int gint()
12 {
13     int rett=0,fh=1;char c=getchar();
14     while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();}
15     while(c>='0'&&c<='9'){rett=(rett<<3)+(rett<<1)+c-'0';c=getchar();}
16     return rett*fh;
17 }
18 void clr()
19 {
20     memset(a,0,sizeof(a));memset(tr,0,sizeof(tr));
21     memset(b,0,sizeof(b));memset(rk,0,sizeof(rk));
22     memset(h,0,sizeof(h));memset(sa,0,sizeof(sa));
23     memset(hs,0,sizeof(hs));
24 }
25 bool check(int k,int x,int y){
26     if(x+k>len||y+k>len) return 0;
27     else return (rk[x]==rk[y]&&rk[x+k]==rk[y+k])?1:0;
28 }
29 void get_sa()
30 {
31     int cnt=0,i=0;
32     for(i=1;i<=n;i++) hs[b[i]]++;
33     for(i=1;i<=2*maxn;i++) if(hs[i]) tr[i]=++cnt;
34     for(i=1;i<=2*maxn;i++) hs[i]+=hs[i-1];
35     for(i=1;i<=n;i++) rk[i]=tr[b[i]],sa[hs[b[i]]--]=i;
36     for(int k=1;cnt<len;k<<=1)
37     {
38         for(i=1;i<=cnt;i++) hs[i]=0;
39         for(i=1;i<=len;i++) hs[rk[i]]++;
40         for(i=1;i<=cnt;i++) hs[i]+=hs[i-1];
41         for(i=len;i>=1;i--) if(sa[i]>k) tr[sa[i]-k]=hs[rk[sa[i]-k]]--;
42         for(i=1;i<=k;i++) tr[len-i+1]=hs[rk[len-i+1]]--;
43         for(i=1;i<=len;i++) sa[tr[i]]=i;
44         for(i=1,cnt=0;i<=len;i++) tr[sa[i]]=check(k,sa[i],sa[i-1])?cnt:++cnt;
45         for(i=1;i<=len;i++) rk[i]=tr[i];
46     }
47 }
48 void get_height()
49 {
50     for(int i=1;i<=len;i++){
51         if(rk[i]==1) continue;
52         for(int j=max(1,h[rk[i-1]]-1);j;j++)
53             if(b[i+j-1]==b[sa[rk[i]-1]+j-1]) h[rk[i]]=j;
54             else break;
55     }
56 }
57 bool check(int ans)
58 {
59     int i=1,j=1;
60     for(i=1;i<=len;)
61     {
62         if(h[i]<ans) {i++;continue;}
63         int mi=sa[i-1],ma=sa[i-1];
64         for(;h[i]>=ans&&i<=len;i++)
65         {
66             mi=min(mi,sa[i]);
67             ma=max(ma,sa[i]);
68             if(ma-mi>ans) return 1;
69         }
70     }return 0;
71 }
72 
73 int main()
74 {
75     while(scanf("%d",&n)&&n!=0)
76     {
77         len=n;
78         clr();
79         for(int i=1;i<=n;i++)
80             a[i]=gint(),b[i]=a[i]-a[i-1]+maxn;
81         get_sa();
82         get_height();
83         int l=0,r=n,ans=0;
84         while(l<=r){
85             int mid=(l+r)>>1;
86             if(check(mid)) ans=mid,l=mid+1;
87             else r=mid-1;
88         }
89         if(ans<4) printf("0
");
90         else printf("%d
",ans+1);
91     }
92     return 0;
93 }
原文地址:https://www.cnblogs.com/guapisolo/p/9697158.html