1. 程式人生 > >Musical Theme(字尾自動機)

Musical Theme(字尾自動機)

Musical Theme

Time Limit: 1000MS
Memory Limit: 30000K

題目大意:
給定一個串,找出滿足條件最長子串:
1,長度大於等於5
2,至少出現兩次
3,至少有兩個出現位置不重疊

子串不一定要嚴格相等,兩兩差值相等即可。
例如:
1 2 3 4 6 = 11 12 13 14 16

n 20000 1 a i 88

Sample Input

30
25 27 30 34 39 45 52 60 69 79 69 60 52 45 39 34 30 26 22 18 82 78 74 70 66 67 64 60 65 80
0

Sample Output

5













解:

每個位置作差插入字尾自動機。找長度大於等於5並且出現至少兩次的子串。
如何滿足條件不重疊?
可以發現一個性質:一個點的right集合為所有parent鏈指向它的點的並集。初始非複製節點的right集合大小為1,可以知道right的位置,對於每個節點記錄right集合的最大和最小位置。拓撲序上推就行。
每個點的答案就是max-min和len+1取min。
因為作差後子串不重疊不一定原串不重疊,隨便寫一個串可以看出來。

本題重要理解:一個點的right集合為所有fail鏈指向它的點的並集,大小為它們的和!!!

code:
本來以為轉移多了要用map,節省空間,結果TLE了。。。
改成暴力陣列存就過了?

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
struct lxy{
    int p,len,min,max;
    int to[180];
}a[40005];

int n,cnt=1,las=1,ans;
int data[20005];
int tax[20005],tp[40005];

void insert(int c,int w){
    a[++cnt].len=w;a[cnt].min=w,a[cnt].max=w;
    int i;for(i=las;a[i].to[c]==0&&i!=0;i=a[i].p) a[i].to[c]=cnt;
    las=cnt;
    if(i==0){
        a[las].p=1;return;
    }
    int q=a[i].to[c],nq;
    if(a[i].len+1==a[q].len){
        a[las].p=q;return;
    }
    nq=++cnt;
    a[nq]=a[q];a[nq].len=a[i].len+1;
    a[nq].min=10000000;a[nq].max=0;
    for(int j=i;a[j].to[c]==q;j=a[j].p) a[j].to[c]=nq;
    a[q].p=nq;a[las].p=nq;
}

void findtp(){
    for(int i=1;i<=cnt;i++) tax[a[i].len]++;
    for(int i=1;i<=n;i++) tax[i]+=tax[i-1];
    for(int i=1;i<=cnt;i++) tp[tax[a[i].len]--]=i;
}

void readit(int &x){
    x=0;char g=getchar();
    while(g<'0'||g>'9') g=getchar();
    while(g>='0'&&g<='9') x=x*10+g-'0',g=getchar();
}

int main()
{
    while(scanf("%d",&n)){
        if(n==0) break;
        for(int i=1;i<=n;i++)
          readit(data[i]);
        for(int i=1;i<n;i++)
          insert(data[i+1]-data[i]+88,i);
        findtp();
        for(int i=cnt;i>=1;i--){
            int k=min(a[tp[i]].len+1,a[tp[i]].max-a[tp[i]].min);
            if(k>=5) ans=max(ans,k);
            a[a[tp[i]].p].max=max(a[a[tp[i]].p].max,a[tp[i]].max);
            a[a[tp[i]].p].min=min(a[a[tp[i]].p].min,a[tp[i]].min);
        }
        printf("%d\n",ans);
        memset(a,0,sizeof(a));
        for(int i=1;i<=n;i++) tax[i]=0;
        for(int i=1;i<=cnt;i++) tp[i]=0;
        cnt=1,las=1,ans=0;
    }
    return 0;
}