[POJ1743]Musical Theme

题目大意:
  给你一个数列,我们数列中两个串是相似的当且仅当这两个串的长度至少为5、两个串差分后形成的数列一样且不相交,求最长的相似串的长度。

思路:
  后缀自动机求最长不相交重复子串。
  首先将差分后的数列构造SAM,然后按照拓扑序DP,得到每个状态right集合的最大值t与最小值s,表示当前状态对应的最长的不相交子串为[s,t]。
  然而我们还要保证以当前状态结尾的子串的长度能够与[s,t]子串相等,设当前状态对应的最长子串长度为len,则最长相似串的长度为min(t-s+1,len) 。
  然后取max即可。

 1 #include<cstdio>
 2 #include<cctype>
 3 #include<cstring>
 4 #include<algorithm>
 5 inline int getint() {
 6     char ch;
 7     while(!isdigit(ch=getchar()));
 8     int x=ch^'0';
 9     while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
10     return x;
11 }
12 const int inf=0x7fffffff;
13 const int LEN=20001;
14 int n;
15 class SuffixAutomaton {
16     private:
17         static const int SIGMA_SIZE=180;
18         struct State {
19             int link,go[SIGMA_SIZE],len,maxright,minright;
20         };
21         State s[LEN<<1];
22         int sz,newState(const int l) {
23             sz++;
24             s[sz].len=l;
25             s[sz].minright=inf;
26             return sz;
27         }
28         int root,last;
29         int cnt[LEN],top[LEN<<1];
30         void tsort() {
31             for(int i=1;i<=sz;i++) cnt[s[i].len]++;
32             for(int i=n;i;i--) cnt[i-1]+=cnt[i];
33             for(int i=1;i<=sz;i++) top[cnt[s[i].len]--]=i;
34         }
35     public:
36         void reset() {
37             sz=0;
38             memset(s,0,sizeof s);
39             root=last=newState(0);
40             memset(cnt,0,sizeof cnt);
41         }
42         void extend(const int w) {
43             int p=last,new_p=newState(s[last].len+1);
44             s[new_p].maxright=s[new_p].minright=s[new_p].len;
45             while(p&&!s[p].go[w]) {
46                 s[p].go[w]=new_p;
47                 p=s[p].link;
48             }
49             if(!p) {
50                 s[new_p].link=root;
51             } else {
52                 int q=s[p].go[w];
53                 if(s[q].len==s[p].len+1) {
54                     s[new_p].link=q;
55                 } else {
56                     int new_q=newState(s[p].len+1);
57                     memcpy(s[new_q].go,s[q].go,sizeof s[q].go);
58                     s[new_q].link=s[q].link;
59                     s[q].link=s[new_p].link=new_q;
60                     while(p&&s[p].go[w]==q) {
61                         s[p].go[w]=new_q;
62                         p=s[p].link;
63                     }
64                 }
65             }
66             last=new_p;
67         }
68         int stat() {
69             tsort();
70             for(int i=1;i<=sz;i++) {
71                 int p=top[i],q=s[top[i]].link;
72                 s[q].maxright=std::max(s[q].maxright,s[p].maxright);
73                 s[q].minright=std::min(s[q].minright,s[p].minright);
74             }
75             int ret=0;
76             for(int i=1;i<=sz;i++) {
77                 ret=std::max(ret,std::min(s[i].maxright-s[i].minright+1,s[i].len+1));
78             }
79             return ret>=5?ret:0;
80         }
81 };
82 SuffixAutomaton sam;
83 int s[LEN];
84 int main() {
85     for(;;) {
86         n=getint();
87         if(!n) return 0;
88         sam.reset();
89         for(int i=1;i<=n;i++) s[i]=getint();
90         for(int i=1;i<n;i++) sam.extend(s[i]=s[i+1]-s[i]+88);
91         printf("%d\n",sam.stat());
92     }
93 } 

 

转载于:https://www.cnblogs.com/skylee03/p/7522785.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值