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 }

 

posted @ 2019-02-17 09:14  Let_Life_Stop  阅读(206)  评论(0编辑  收藏  举报