Musical Theme(后缀自动机)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lvmaooi/article/details/82693985

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

猜你喜欢

转载自blog.csdn.net/lvmaooi/article/details/82693985
今日推荐