【GDOI2014 DAY2】Beyond (扩展KMP)

【题目】

 

 

【题意】

  Jodie和Aiden在做游戏。Jodie在一个长度为l字符串环上走路,他每离开一个就会记下格子当前字符。他让Aiden在他走了一圈后叫他停下来。Aiden决定耍一下Jodie,在他走了k步重复的格后才告诉他。Jodie离开的格子会随机变为一个字符。Jodie走了两次(起点可能不同),每次都走了n(即l+k)步。给出两个长度n的字符串,表示Jodie两次记录的字符串,问l最大可以是多少。 N<=100000

 

【分析】

  做2次扩展KMP,枚举第二个串的第i位与第一个串对应。一开始容易走入的误区就是直接使用tend2[i]然后判断,但是我们其实不一定让i往后的串越长越好,因为可能前面匹配不了。但是可以确定的是前面的匹配长度已经固定了,即i-1,所以我们只要在第一个串中找到所有的tend1大于等于i-1的x,当x越大,匹配长度越大,所以我们找最大的x使得其tend1[x]大于等于i-1。 可以用二分+rmq或者线段树。 也可以一开始排个序,然后用树状数组动态加减,然后用二分查找。

 

 

再放一次扩展KMP部分的代码:(注意那个小于号和小于等于号那里!很重要!):

void get_nt(int x)
{
	nt[1]=n;
	int mx=0,id;
	while(s[x][1+mx]==s[x][2+mx]&&mx<=n) mx++;
	nt[2]=mx;id=2;
	for(int i=3;i<=n;i++)
	{
		int now=nt[i-id+1];
		if(i+now-1<mx) nt[i]=now;//-> i+now<=mx 注意不要写成i+now-1<=mx!!!
		else
		{
			int j=mx-i+1;
            if(j<0) j=0;
            while(i+j<=n&&s[x][i+j]==s[x][1+j]) j++;
            nt[i]=j;
			id=i;mx=i+nt[i]-1;
		}
	}
}

void get_td(int x,int y)
{
	int mx=0,id;
	while(s[x][1+mx]==s[y][1+mx]) mx++;
	td[y][1]=mx;id=1;
	for(int i=2;i<=n;i++)
	{
		int now=nt[i-id+1];
		if(i+now-1<mx) td[y][i]=now;
		else //i+now-1>=mx
		{
			int j=mx-i+1;
			if(j<0) j=0;
			while(i+j<=n&&s[x][1+j]==s[y][i+j]) j++;
			td[y][i]=j;
			id=i;mx=i+td[y][i]-1;
		}
	}
}

  

 

代码如下:

  1 #include<cstdio>
  2 #include<cstdlib>
  3 #include<cstring>
  4 #include<iostream>
  5 #include<algorithm>
  6 using namespace std;
  7 #define Maxn 2000010
  8 
  9 int n;
 10 char s[2][Maxn];
 11 int nt[Maxn],td[2][Maxn];
 12 
 13 int c[Maxn];
 14 struct node
 15 {
 16     int x,y;
 17 }t[Maxn];
 18 
 19 bool cmp(node x,node y) {return x.y<y.y;}
 20 
 21 int mymax(int x,int y) {return x>y?x:y;}
 22 
 23 void get_nt(int x)
 24 {
 25     nt[1]=n;
 26     int mx=0,id;
 27     while(s[x][1+mx]==s[x][2+mx]&&mx<=n) mx++;
 28     nt[2]=mx;id=2;
 29     for(int i=3;i<=n;i++)
 30     {
 31         // mx=id+nt[id]-1;
 32         int now=nt[i-id+1];
 33         if(i+now-1<mx) nt[i]=now;//-> i+now<=mx 注意不要写成i+now-1<=mx!!!
 34         else
 35         {
 36             int j=mx-i+1;
 37             if(j<0) j=0;
 38             while(i+j<=n&&s[x][i+j]==s[x][1+j]) j++;
 39             nt[i]=j;
 40             id=i;mx=i+nt[i]-1;
 41         }
 42     }
 43 }
 44 
 45 void get_td(int x,int y)
 46 {
 47     int mx=0,id;
 48     while(s[x][1+mx]==s[y][1+mx]) mx++;
 49     td[y][1]=mx;id=1;
 50     for(int i=2;i<=n;i++)
 51     {
 52         int now=nt[i-id+1];
 53         if(i+now-1<mx) td[y][i]=now;
 54         else //i+now-1>=mx
 55         {
 56             int j=mx-i+1;
 57             if(j<0) j=0;
 58             while(i+j<=n&&s[x][1+j]==s[y][i+j]) j++;
 59             td[y][i]=j;
 60             id=i;mx=i+td[y][i]-1;
 61         }
 62     }
 63 }
 64 
 65 void add(int x,int y)
 66 {
 67     for(int i=x;i<=n;i+=i&(-i))
 68      c[i]+=y;
 69 }
 70 
 71 int get_sum(int x)
 72 {
 73     int ans=0;
 74     for(int i=x;i>=1;i-=i&(-i))
 75      ans+=c[i];
 76     return ans;
 77 }
 78 
 79 int ffind(int r)
 80 {
 81     int l=1;
 82     while(l<r)
 83     {
 84         int mid=(l+r)>>1;
 85         if(get_sum(r)-get_sum(mid)>0) l=mid+1;
 86         else r=mid;
 87     }
 88     if(get_sum(l)==0) return 0;
 89     return l;
 90 }
 91 
 92 int main()
 93 {
 94     scanf("%d",&n);
 95     scanf("%s%s",s[0]+1,s[1]+1);
 96     get_nt(0);get_td(0,1);
 97     
 98     get_nt(1);get_td(1,0);
 99     
100     memset(c,0,sizeof(c));
101     for(int i=1;i<=n;i++) 
102     {
103         t[i].x=i;
104         t[i].y=td[0][i];
105         add(i,1);
106     }
107     sort(t+1,t+1+n,cmp);
108     
109     
110     int now=1,ans=0;
111     for(int i=1;i<=n;i++)
112     {
113         while(t[now].y<i-1&&now<=n)
114         {
115             add(t[now].x,-1);
116             now++;
117         }
118         int x=ffind(td[1][i]+1);
119         if(x) ans=mymax(ans,x+i-2);
120     }
121     printf("%d\n",ans);
122     return 0;
123 }
[BEYOND]

 

2016-08-20 10:55:34

 

posted @ 2016-08-20 10:53  konjak魔芋  阅读(346)  评论(0编辑  收藏  举报