【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

时间: 2024-08-21 05:04:13

【GDOI2014 DAY2】Beyond (扩展KMP)的相关文章

扩展KMP复习小记

简介 KMP大家都耳熟能详,扩展KMP只是一个扩展版而已,字面意思啦! 我记得以前打过这个复习小记的,但是不知为何失踪了. KMP与扩展KMP的对比 KMP的next[i]表示从1到i的字符串s,前缀和后缀的最长重叠长度. EXKMP的next[i]表示从1到i的字符串s,和从i到n的字符串st的最长重叠长度. 也就是说KMP是向前的匹配,EXKMP是向后匹配. 扩展KMP问题是KMP问题的补充和加难. 具体内容 重要数组 给定母串S,和子串T.定义n=|S|, m=|T|. extend[i]

hdu2328 Corporate Identity 扩展KMP

Beside other services, ACM helps companies to clearly state their "corporate identity", which includes company logo but also other signs, like trademarks. One of such companies is Internet Building Masters (IBM), which has recently asked ACM for

扩展KMP算法

扩展KMP,用于求s的后缀的最长前缀.用extand数组表示第i个后缀的最长前缀的字符个数. 注意几点:1.next数组是对T的   2.extand数组是对S的 3.应用:回文,重复串等 代码如下: 1 #include<iostream> 2 #include<string> 3 #include<cstdio> 4 using namespace std; 5 const int MM=100005; //长度最大值 6 int next[MM],extand[M

扩展kmp——原创

扩展kmp                 LRH 所谓扩展kmp指的是与kmp相似的求辅助数组的原理,但是本身与kmp关系不大. 1.exkmp的用途:给定一个主串s和一个子串t,求出s中每一个后缀和子串t的最长公共前缀. 2.算法推导: 给定一个主串:S=aaaaaaaaaabaaa  T=aaaaaaaaaaa (下标都是从零开始!!!)                  第一步 需要有两个辅助数组:extand[i]和next[i]: extand[i]:表示主串S以i开始的后缀与子串T

扩展KMP模板

扩展KMP:    给出模板串A和子串B,长度分别为lenA和lenB,要求在线性时间内,对于每个A[i](0 <= i < lenA),求出A[i..lenA-1]与B的最长公共前缀长度,记为ex[i](或者说,ex[i]为满足A[i..i + z - 1]==B[0 .. z - 1]的最大的z值).    扩展KMP可以用来解决很多字符串问题,如求一个字符串的最长回文子串和最长重复子串.[算法]    设next[i]为满足B[i..i + z - 1] == B[0..z - 1]的最

扩展KMP - HDU 4333 Revolving Digits

Revolving Digits Problem's Link: http://acm.hdu.edu.cn/showproblem.php?pid=4333 Mean: 给你一个字符串,你可以将该字符串的任意长度后缀截取下来然后接到最前面,让你统计所有新串中有多少种字典序小于.等于.大于原串. analyse: KMP的经典题. 首先我们将原串扩展成两倍,算一遍扩展KMP(自匹配),时间复杂度O(n). 这样一来,我们就得到了eKMP[i],eKMP[i]代表s[i...len-1]与s的最长

HDU 4300 (扩展KMP或KMP)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4300 题意:说实话,看半天题目,愣是没看懂,百度题意才看懂,大概意思就是:第一个串串为匹配串,第i个代表字母顺序中的第i个,比如第一个是q,那么,q就代表a. 第二个串是密文+明文,密文全部都有,但明文可能不全,输出完整的密文+明文. 题解:有两种做法,第一种,用扩展KMP: 1.先把s2砍半,前面一半必定为密文,后面一半可能是密文+明文. 2.把前面的一半转化成明文. 3.用后面的和前面的找最长公

浅谈Manacher算法与扩展KMP之间的联系

首先,在谈到Manacher算法之前,我们先来看一个小问题:给定一个字符串S,求该字符串的最长回文子串的长度.对于该问题的求解,网上解法颇多,时间复杂度也不尽相同,这里列述几种常见的解法. 解法一 通过枚举S的子串,然后判断该子串是否为回文,由于S的子串个数大约为,加上每次判断需要的时间,所以总的时间复杂度为,空间复杂度为. bool check(string &S, int left, int right) { while (left < right && S[left]

Part.5【马拉车&amp;扩展KMP】

Manacher(马拉车)是一种求最长回文串的线性算法,复杂度O(n).网上对其介绍的资料已经挺多了的,请善用搜索引擎. 而扩展KMP说白了就是是求模式串和主串的每一个后缀的最长公共前缀[KMP更像是一个自动机] 题目: POJ 1159: Palindrome 求原字符串最少增加几个字符后可变成回文串,相当于求最长回文子序列的长度. 解法:直接求串S和反转串Sr的最长公共子序列. #include <cstdlib> #include <cstdio> #include <