Codeforces 700E Cool Slogans 后缀数组+线段树

来源:互联网 发布:windows声音设置 编辑:程序博客网 时间:2024/05/16 19:28

E. Cool Slogans
time limit per test
4 seconds
memory limit per test
512 megabytes
input
standard input
output
standard output

Bomboslav set up a branding agency and now helps companies to create new logos and advertising slogans. In term of this problems, slogan of the company should be a non-empty substring of its name. For example, if the company name is "hornsandhoofs", then substrings "sand" and "hor" could be its slogans, while strings "e" and "hornss" can not.

Sometimes the company performs rebranding and changes its slogan. Slogan A is considered to be coolerthan slogan B if B appears in A as a substring at least twice (this occurrences are allowed to overlap). For example, slogan A =  "abacaba" is cooler than slogan B =  "ba", slogan A =  "abcbcbe" is cooler than slogan B =  "bcb", but slogan A =  "aaaaaa" is not cooler than slogan B =  "aba".

You are given the company name w and your task is to help Bomboslav determine the length of the longest sequence of slogans s1, s2, ..., sk, such that any slogan in the sequence is cooler than the previous one.

Input

The first line of the input contains a single integer n (1 ≤ n ≤ 200 000) — the length of the company name that asks Bomboslav to help. The second line contains the string w of length n, that consists of lowercase English letters.

Output

Print a single integer — the maximum possible length of the sequence of slogans of the company named w, such that any slogan in the sequence (except the first one) is cooler than the previous

Examples
input
3abc
output
1
input
5ddddd
output
5
input
11abracadabra
output
3

定义如果短串在另一个长串当中出现了至少两次,则长串比短串更酷。构造一个序列使得其中每个串都比后一个串酷,要求序列当中每个串都是给定串的子串,求这个序列的最长长度。


搞了一下午。

英文题解如下:

Let's call a good substring either a single character, or a substring starting and ending with a smaller good substring, with no other occurence of that smaller substring in the middle. A maximal chain can be transformed into another maximal chain of good substrings, so we only need to consider those.

The repeated substring of a good substring is the previous good substring in the chain.

Actually, there are exacly n good substrings : for each position i, we can consider the largest good substring starting there. It can't appear right of it, or we would have a larger good substring starting at i, and if it appears left, it can be matched with the occurence at i to make a larger substring. It also means we can build a tree on good substrings (i.e. on string positions), with an edge from s1 to s2 if s1 is the repeated substring in s2. We are interested in the depth of that tree.

Let's build the tree incrementally. We process the string from right to left (in increasing size of suffixes). We want to be able to compute at each position the size of the good substring. Assume we are at position i, with parent j. We know that substring s starts at i and j and we want to compute the size of the smallest substring starting at i with two occurences of s, i.e. the substring with exactly two occurences of s. Let's assume for now that we can know the last time we saw s. Then we know the size of the good substring of i. We can use the suffix array to find all other positions starting with s, and set i as their potential parent (i will only end up as an ancestor).

So the only thing left is to compute the last time we saw a good substring. When we see a substring, we can propagate our position upwards in the tree (update all its ancestors with it). It is however too slow, as the tree can have depth O(n). We can transform this by setting a value in one node and doing subtree minimum queries. Subtree minimum queries can be solved using a segment tree on a static tree, but here building the tree and queries are interleaved. A solution could be to use an euler tour tree, but it is actually possible to use a segment tree in this case, as even though we don't know the shape of the tree in advance, each time we add a node, we know the size of its subtree (the number of substrings starting with its good substring), so we can give it a range in the segment tree with the same size.

大意如此:

从后向前扫描字符串,对于字符串每个位置 i 我们只要记录从这个位置开始的最大答案。

对于每个位置 i ,我们假设现在已知最大的答案 t , 设从 i 开始的,答案为 t 的,长度最短的子串为s.那么t=1时length(s)=1,否则s的首尾必定包含相同的子串。我们用这个 t 在以rank为下标的线段树上更新首尾都包含s的串,这些串的范围,我们可以二分得到。具体操作是,在后缀数组以rank为下标,去找和s的height为length(s)的、包含s的区间[L,R].由于长度短的串在答案相同时更优,所以要同时更新s的长度。

那么对于每个位置 i ,我们在线段树上单点查询,这个点的最大答案就是查询结果+1.接下来,我们只要确定s的长度就好了。同样的二分height至少为在s首尾都出现的子串的length的一段[L,R],在线段树的这一段寻找开头位置最小的一个。接着,就能算出s的长度。


#include <cstdio>#include <string.h>#include <string> #include <algorithm>#define mem0(a) memset(a,0,sizeof(a))#define meminf(a) memset(a,0x3f,sizeof(a))using namespace std;typedef long long ll;typedef long double ld;typedef double db;const int maxn=200005,inf=0x3f3f3f3f;  const ll llinf=0x3f3f3f3f3f3f3f3f;   int wa[maxn],wb[maxn],wv[maxn],wss[maxn],sa[maxn],ranki[maxn],height[maxn];char s[maxn];int a[maxn],mn[maxn][20];int num,L,R;struct node {int max, len;};struct Tree {int lc,rc,l,r,min;node c;};Tree tree[4*maxn];node MAX(node a, node b) {if ((a.max > b.max || (a.max == b.max&&a.len < b.len))) return a; else return b;}int cmp(int *r,int a,int b,int l) {return r[a]==r[b]&&r[a+l]==r[b+l];}void build(int *r,int *sa,int n,int m) {int i,j,k,p,*x=wa,*y=wb,*t;for (i=0;i<m;i++) wss[i]=0;for (i=0;i<n;i++) wss[x[i]=r[i]]++;for (i=0;i<m;i++) wss[i]+=wss[i-1];for (i=n-1;i>=0;i--) sa[--wss[x[i]]]=i;for (j=1,p=1;p<n;j*=2,m=p) {for (p=0,i=n-j;i<n;i++) y[p++]=i;for (i=0;i<n;i++) if (sa[i]>=j) y[p++]=sa[i]-j;for (i=0;i<n;i++) wv[i]=x[y[i]];for (i=0;i<m;i++) wss[i]=0;for (i=0;i<n;i++) wss[wv[i]]++;for (i=1;i<m;i++) wss[i]+=wss[i-1];for (i=n-1;i>=0;i--) sa[--wss[wv[i]]]=y[i];t=x;x=y;y=t;p=1;x[sa[0]]=0;for (i=1;i<n;i++)  x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;}for (i=1;i<n;i++) ranki[sa[i]]=i;k=0; for (i=0;i<n-1;height[ranki[i++]]=k) {if (k) k--;for (j=sa[ranki[i]-1];r[i+k]==r[j+k];k++);}}void rmq(int n) {int i,j;for (i=1;i<=n;i++) mn[i][0]=height[i];for (j=1;(1<<j)<=n;j++) {for (i=1;i+(1<<j)-1<=n;i++) {mn[i][j]=min(mn[i][j-1],mn[i+(1<<(j-1))][j-1]);}}}int lcp(int fa,int fb) {if (fa>fb) swap(fa,fb);fa++;int k=0;while ((1<<(k+1))<=(fb-fa+1)) k++;return min(mn[fa][k],mn[fb-(1<<k)+1][k]);}void buildtree(int now,int l,int r) {tree[now].l=l;tree[now].r=r;tree[now].c.max=tree[now].c.len=0;tree[now].min=inf;if (l!=r) {num++;tree[now].lc=num;buildtree(num,l,(l+r)/2);num++;tree[now].rc=num;buildtree(num,(l+r)/2+1,r);}}void update1 (int now,int l,int r,int val,int len) {if (tree[now].l>=l&&tree[now].r<=r) {if (val>tree[now].c.max||(val==tree[now].c.max&&len<tree[now].c.len))tree[now].c.max=val,tree[now].c.len=len;} else {if (l<=(tree[now].l+tree[now].r)/2)     update1(tree[now].lc,l,r,val,len);if (r>(tree[now].l+tree[now].r)/2)    update1(tree[now].rc,l,r,val,len);//tree[now].c = MAX(tree[tree[now].lc].c, tree[tree[now].rc].c);}}void update2 (int now,int pos,int val) {if (tree[now].l==pos&&tree[now].r==pos) {tree[now].min=val;} else {if (pos<=(tree[now].l+tree[now].r)/2)     update2(tree[now].lc,pos,val);if (pos>(tree[now].l+tree[now].r)/2)    update2(tree[now].rc,pos,val);tree[now].min=min(tree[tree[now].lc].min,tree[tree[now].rc].min);}}node findval (int now,int pos) {if (tree[now].l==pos&&tree[now].r==pos) return tree[now].c;else if (pos<=(tree[now].l+tree[now].r)/2)     return MAX(tree[now].c, findval(tree[now].lc, pos));else    return MAX(tree[now].c,findval(tree[now].rc, pos));}int findmin(int now,int l,int r) {if (tree[now].l>=l&&tree[now].r<=r) {return tree[now].min;} else {int ans=inf;if (l<=(tree[now].l+tree[now].r)/2)     ans=min(ans,findmin(tree[now].lc,l,r));if (r>(tree[now].l+tree[now].r)/2)    ans=min(ans,findmin(tree[now].rc,l,r));return ans;}}void BinarySearch(int pos,int len,int n) {int l=1,r=pos-1;L=pos;while (l<=r) {int mid=(l+r)/2;if (lcp(mid,pos)>=len) L=mid,r=mid-1; else l=mid+1;}l=pos+1,r=n;R=pos;while (l<=r) {int mid=(l+r)/2;if (lcp(pos,mid)>=len) R=mid,l=mid+1; else r=mid-1;}}int main() {int n,i,ans=1,val,len;scanf("%d",&n);scanf("%s",s);for (i=0;i<n;i++) {a[i]=s[i]-'a'+1;}a[n]=0;build(a,sa,n+1,27);rmq(n+1);num=1;buildtree(1,1,n);for (i=n-1;i>=0;i--) {node now=findval(1,ranki[i]);if (now.max==0) val=1,len=1; else {val=now.max+1;BinarySearch(ranki[i],now.len,n);len=now.len+findmin(1,L,R)-i;}update2(1,ranki[i],i);BinarySearch(ranki[i],len,n);update1(1,L,R,val,len);ans=max(ans,val);}printf("%d\n",ans);//system("pause");return 0;}

阅读全文
0 0
原创粉丝点击