C - Musical Theme (后缀数组)
题目链接:https://cn.vjudge.net/contest/283743#problem/C
题目大意:给你n个数组,然后问你是否有多个“相似”且不重叠的子串的长度大于等于5(两个子串相似当且仅当长度相等且每一位的数字差都相等)。
具体思路:对于相似,我们直接对于当前的输入的和他的上一位相减就可以了,这样差就表示出来了,然后就开始判断时候有相同的子串并且不重叠并且长度>=5就可以了。判断的时候,首先分组,分组的标准是这个组里面的height是不是大于等k,对于每一个组,找出最大的sa差值,然后就判断差值和5的关系就可以了。对于具体的原因,height[i]数组指的是排序之后,排名为i和i-1的最长公共前缀,然后再分组的时候,如果height数组>=k的时候,就直接判断这两个字符串的开头位置就可以了,这个可以通过sa数组实现。
AC代码:
1 #include<iostream> 2 #include<stack> 3 #include<cstring> 4 #include<iomanip> 5 #include<stdio.h> 6 #include<algorithm> 7 #include<cmath> 8 using namespace std; 9 # define ll long long 10 # define inf 0x3f3f3f3f 11 const int maxn = 5e5+100; 12 int cntA[maxn], cntB[maxn], sa[maxn], tsa[maxn], A[maxn], B[maxn], height[maxn]; 13 int Rank[maxn]; 14 int ch[maxn]; 15 int sto[maxn]; 16 ll n; 17 //sa[i]代表第i小的后缀位置,Rank[i]代表第i位置的后缀,排名第几小 18 // height[i]代表排名第i个字符串和第i-1个字符串的相同前缀有多少个 19 void cal() 20 { 21 for(int i = 0; i < 256; i++) 22 cntA[i] = 0; 23 // cout<<1<<endl; 24 // cout<<n<<endl; 25 for(int i = 1; i <= n; i++){ 26 //cout<<ch[i-1]<<endl; 27 cntA[ch[i-1]]++; 28 } 29 // cout<<1<<endl; 30 for(int i = 1; i < 256; i++) 31 cntA[i] += cntA[i-1]; 32 for(int i = n; i; i--) 33 sa[cntA[ch[i-1]]--] = i; 34 Rank[sa[1]] = 1; 35 for(int i = 2; i <= n; i++) 36 { 37 Rank[sa[i]] = Rank[sa[i-1]]; 38 if(ch[sa[i]-1] != ch[sa[i-1]-1]) 39 Rank[sa[i]]++; 40 } 41 for(int l = 1; Rank[sa[n]] < n; l <<= 1) 42 { 43 memset(cntA, 0, sizeof(cntA)); 44 memset(cntB, 0, sizeof(cntB)); 45 for(int i = 1; i <= n; i++) 46 { 47 cntA[A[i] = Rank[i]]++; 48 cntB[B[i] = (i+l <= n)?Rank[i+l]:0]++; 49 } 50 for(int i = 1; i <= n; i++) 51 cntB[i] += cntB[i-1]; 52 for(int i = n; i; i--) 53 tsa[cntB[B[i]]--] = i; 54 for(int i = 1; i <= n; i++) 55 cntA[i] += cntA[i-1]; 56 for(int i = n; i; i--) 57 sa[cntA[A[tsa[i]]]--] = tsa[i]; 58 Rank[sa[1]]=1; 59 for(int i = 2; i <= n; i++) 60 { 61 Rank[sa[i]] = Rank[sa[i-1]]; 62 if(A[sa[i]] != A[sa[i-1]] || B[sa[i]] != B[sa[i-1]]) 63 Rank[sa[i]]++; 64 } 65 } 66 for(int i = 1, j = 0; i <= n; i++) 67 { 68 if(j) 69 j--; 70 while(ch[i+j-1] == ch[sa[Rank[i]-1] + j - 1]) 71 j++; 72 height[Rank[i]] = j; 73 } 74 } 75 bool judge(int t) 76 { 77 int mx=-inf,mm=inf; 78 for(int i=2; i<=n; i++) 79 { 80 if(height[i]>=t) 81 { 82 mm=min(mm,min(sa[i],sa[i-1])); 83 mx=max(mx,max(sa[i],sa[i-1])); 84 if(mx-mm>t) 85 return true; 86 } 87 else 88 { 89 mm=inf; 90 mx=-inf; 91 } 92 } 93 return false; 94 } 95 int main() 96 { 97 // int n; 98 while(scanf("%d",&n)&&n) 99 { 100 for(int i=0; i<n; i++) 101 { 102 scanf("%d",&sto[i]); 103 } 104 for(int i=0; i<n; i++) 105 { 106 ch[i]=sto[i+1]-sto[i]+90; 107 } 108 n--; 109 cal(); 110 int ans=-1; 111 int l=0,r=1e8; 112 while(l<=r) 113 { 114 int mid=(l+r)>>1; 115 // cout<<l<<" "<<r<<endl; 116 if(judge(mid)) 117 { 118 ans=mid; 119 l=mid+1; 120 // cout<<ans<<endl; 121 } 122 else 123 { 124 r=mid-1; 125 } 126 } 127 if(ans>=4) 128 printf("%d\n",ans+1); 129 else 130 { 131 printf("0\n"); 132 } 133 } 134 return 0; 135 }