poj1743 Musical Theme(字尾陣列)
阿新 • • 發佈:2018-12-11
題意:
給你N個數,他們之間的升降調可以定義為相鄰兩數之差,但旋律可以整體升階或降階,問最少五個數長的相同旋律。
心得:
這題可以說是很毒瘤了,WA了若干發終於A了,期間竟然還有RE。
先作差分陣列,可以看出長度小於10的顯然不夠分剪枝減掉。
然後,二分列舉答案,L為0,R為N/2。
這題即求 不重複的最長公共子串,考慮到2 4 6 8 10作出差分陣列為4個2,即最終答案大於等於4即可。
如果是重複的最長公共子串,只需輸出height的最大值即可。
畢竟height的sa不相鄰字首最大值是相鄰的最小值,則結果一定是sa相鄰的。
細節部分:
①作差可以作出負的,考慮到1-88的旋律,對差分陣列整體+90,這樣元素就分佈到0-200之間。
②差分陣列的實際長度,加上後補0的長度,最後還是N,預處理sa陣列與high陣列要注意。
③C陣列,基數排序臨時存排名的數組別開小了,開200直接RE,我也不知道為啥。
④對於連續的high陣列,只要讓最末SA排名對應字首起始位置tall和最初SA排名對應字首起始位置low之間,差出一個二分的mid答案即可,即若s[3]-s[5]相同,s[6]-s[8]相同,且二者公共字首長度為3,只需讓tall-low≥(5-3+1)即可,則二者沒有重疊部分。 對於aaaabb,aaaabc,aaaacc這種連續相同字首,只需讓最大-最小≥len即可。
if(high[i]<mid)low=sa[i],tall=sa[i]; else { low=min(low,sa[i]); tall=max(tall,sa[i]); if(tall-low>=mid){flag=1;break;} }
完整程式碼:
#include <iostream> #include <algorithm> #include <string> #include <cstring> #include <cstdio> #include <cmath> #include <set> #include <map> #include <vector> #include <stack> #include <queue> #include <bitset> const int INF=0x3f3f3f3f; const int mod=1e9+7; const double eps=1e-7; typedef long long ll; #define vi vector<int> #define si set<int> #define pii pair<int,int> #define pi acos(-1.0) #define pb push_back #define mp make_pair #define lowbit(x) (x&(-x)) #define sci(x) scanf("%d",&(x)) #define scll(x) scanf("%lld",&(x)) #define sclf(x) scanf("%lf",&(x)) #define pri(x) printf("%d",(x)) #define rep(i,j,k) for(int i=j;i<=k;++i) #define per(i,j,k) for(int i=j;i>=k;--i) #define mem(a,b) memset(a,b,sizeof(a)) using namespace std; #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N = 20010; int s[N],v[N]; int sa[N], t[N], t2[N], c[N], n; int Rank[N], high[N],ans; void build(int m) { int i, *x = t, *y = t2; for(i = 0; i < m; i++) c[i] = 0; for(i = 0; i < n; i++) c[x[i] = s[i]]++; for(i = 1; i < m; i++) c[i] += c[i-1]; for(i = n-1; i >= 0; i--) sa[--c[x[i]]] = i; for(int k = 1, p; k <= n; k<<=1, m=p) { p = 0; for(i = n-k; i < n; i++) y[p++] = i; for(i = 0; i < n; i++) if(sa[i] >= k) y[p++] = sa[i] - k; for(i = 0; i < m; i++) c[i] = 0; for(i = 0; i < n; i++) c[x[y[i]]]++; for(i = 1; i < m; i++) c[i] += c[i-1]; for(i = n-1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i]; swap(x, y); p = 1; x[sa[0]] = 0; for(i = 1; i < n; i++) { x[sa[i]] = (y[sa[i-1]] == y[sa[i]] && y[sa[i-1]+k] == y[sa[i]+k]) ? p-1 : p++; } if(p >= n) break; } } /* void get_high() { int j,k=0; for(int i=0;i<n;++i)Rank[sa[i]]=i; for(int i=0;i<n-1;high[Rank[i++]]=k) for(k?k--:0,j=sa[Rank[i]-1];s[i+k]==s[j+k];k++); for(int i=0;i<n;++i)printf("%d:%d %d\n",i,sa[i],high[i]); }*/ void get_height()//求height函式 { int i,j,k=0; for(i=1;i<n;i++)Rank[sa[i]]=i;//求rank函式 for(i=0;i<n;high[Rank[i++]]=k) for(k?k--:0,j=sa[Rank[i]-1];s[i+k]==s[j+k];k++); //for(int i=0;i<n;++i)printf("%d:%d %d\n",i,sa[i],high[i]); } int solve(int t) { int l=0,r=t/2,mid;//二分最終答案長度 bool flag=0; while(l<=r) { mid=(l+r)>>1; int low=sa[1],tall=sa[1]; flag=0; rep(i,2,n-1) { if(high[i]<mid)low=sa[i],tall=sa[i]; else { low=min(low,sa[i]); tall=max(tall,sa[i]); if(tall-low>=mid){flag=1;break;} } } if(flag)l=mid+1; else r=mid-1; } return r>=4?r+1:0; } int main() { while(scanf("%d",&n),n) { for(int i=0;i<n;++i)scanf("%d",&v[i]); if(n<10){puts("0");continue;} for(int i=0;i<n-1;++i)s[i]=v[i+1]-v[i]+90;//最小負值-87 加90為正 s[n-1]=0; int maxi = 0; for(int i = 0; i < n; i++)maxi = maxi > s[i] ? maxi : s[i]; build(maxi+1); get_height(); printf("%d\n",solve(n)); } return 0; }