1. 程式人生 > >poj1743 Musical Theme(字尾陣列)

poj1743 Musical Theme(字尾陣列)

題意:

給你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;
}