acdream1116 Gao the string!(扩展KMP)

今天是字符串填坑的一天,首先填的第一个坑是扩展KMP。总结一下KMP和扩展KMP的区别。

在这里s是主串,t是模式串。

KMP可以求出的是以s[i]为结尾的串和 t前缀匹配的最长的长度。假如这个长度是L的话,则:

s[i-L+1...i]=t[0...L]

而所谓的失配指针f[i]指的就是当前i点失配时要匹配的长度,实际是用t文本串去匹配t。

扩展KMP则是以s[i]为起始的串和 t前缀匹配的最长的长度。 假如这个长度的话,则:

s[i..i+L-1]=t[0...L]

扩展KMP里的nxt数组就是利用t本身和自己匹配达到的效果。所以nxt[i]就是以t的后缀i和t的前缀匹配的最长的长度,有了这个就可以用来求上次的这道题了。

自己写的时候写了个后缀数组的版本,可以将t的前缀理解成后缀0,于是就是后缀之间的最长前缀,就是利用lcp来求,无奈的是后缀数组本身求的速度太慢了,rmq的速度更慢。由于扩展KMP是线性的,所以这次就可以顺利的过了这道坑爹题了。

下面第一个网里有一些理论的证明,但KMP那部分和我学的不太一样,不知道是我搞错了还是版本不一样,然后第二个链接里的代码感觉写的可读性强一点,在这里存一下模板。

http://www.cnblogs.com/10jschen/archive/2012/09/03/2668149.html

http://www.cnblogs.com/kuangbin/archive/2012/08/27/2659246.html

#pragma warning(disable:4996)
#include <iostream>
#include <cstring>
#include <string>
#include <vector>
#include <cmath>
#include <cstdio>
#include <algorithm>
using namespace std;

#define ll long long
#define mxs 1000000
#define mxt 100000
#define mod 1000000007

char s[mxs], t[mxt];

int nxt[mxt], ex[mxt];

// get the next array for t only
void getNext(char *t,int *nxt)
{
	int m = strlen(t);
	nxt[0] = m;
	int j = 0;
	while (j + 1 < m&&t[j] == t[j + 1]) j++;
	nxt[1] = j;
	int k = 1; int p, L;
	for (int i = 2; i < m; i++)
	{
		p = nxt[k] + k - 1;
		L = nxt[i - k];
		if (i + L < p + 1) nxt[i] = L; // i+L<=p
		else
		{
			j = max(0, p - i + 1);
			while (i + j < m&&t[i + j] == t[0 + j])j++;
			nxt[i] = j;
			k = i;
		}
	}
}

// get the next array for t, and get the ex array for s;
void getExtend(char *s, char *t, int *nxt, int *ex)
{
	getNext(t, nxt);
	int n = strlen(s), m = strlen(t);
	int j = 0;
	while (j < n&&j < m&&s[j] == t[j]) j++;
	ex[0] = j;
	int k = 0; int p, L;
	for (int i = 1; i < n; i++){
		p = ex[k] + k - 1;
		L = nxt[i - k];
		if (i + L < p + 1) ex[i] = L;
		else{
			j = max(0, p - i + 1);
			while (i + j < n&&j < m&&s[i + j] == t[j]) j++;
			ex[i] = j;
			k = i;
		}
	}
}

struct Matrix
{
	ll a[2][2];
	Matrix(){ memset(a, 0, sizeof(a)); }
}m;

Matrix operator * (const Matrix &a, const Matrix &b){
	Matrix ret;
	for (int i = 0; i < 2; i++){
		for (int j = 0; j < 2; j++){
			for (int k = 0; k < 2; k++){
				ret.a[i][j] += (a.a[i][k] * b.a[k][j]) % mod;
				ret.a[i][j] %= mod;
			}
		}
	}
	return ret;
}
Matrix operator ^ (Matrix a, ll n){
	Matrix ret;
	for (int i = 0; i < 2; i++) ret.a[i][i] = 1;
	while (n){
		if (n & 1) ret = ret*a;
		n >>= 1;
		a = a*a;
	}
	return ret;
}

ll cal(ll n)
{
	m.a[0][0] = 0; m.a[0][1] = 1;
	m.a[1][0] = 1; m.a[1][1] = 1;
	m = m^n;
	return m.a[0][1];
}

int main()
{
	while (~scanf("%s",s)){
		getNext(s, nxt);
		int n = strlen(s);
		nxt[n] = 0;
		ll ans = 0;
		for (int i = n - 1; i >= 0; i--){
			nxt[i] += nxt[i + 1];
			ans += cal(nxt[i]);
			ans %= mod;
		}
		printf("%lld\n", ans);
	}
	return 0;
}

acdream1116 Gao the string!(扩展KMP)

时间: 2024-10-09 18:56:11

acdream1116 Gao the string!(扩展KMP)的相关文章

ZOJ 3587 Marlon&#39;s String 扩展KMP

链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3587 题意:给出两个字符串S和T,S,T<=100000.拿出S的两个子串(可以重叠),将两个子串连接起来成为字符串T的方法有多少种. 思路:用扩展KMP求出S的从每位开始的子串与T的公共前缀,再将两个子串翻转,再用扩展KMP求出S反的从每位开始的子串与T反的公共前缀.找出其中和为T子串长度的S公共前缀和S反的公共前缀的数量,相乘为结果. 代码: #include

acdream1116 Gao the string!(hash二分 or 后缀数组)

问题套了一个斐波那契数,归根结底就是要求对于所有后缀s[i...n-1],所有前缀在其中出现的总次数.我一开始做的时候想了好久,后来看了别人的解法才恍然大悟.对于一个后缀来说 s[i...n-1]来说,所有与它匹配的前缀必然是和 s[i+1...n-1]  s[i+2...n-1] ....s[n-1..n-1]里的前缀匹配的,因而如果我们定义一个num[i]表示的是后缀s[i...n-1]与前缀的总长公共前缀,那么num[i]+num[i+1]+..num[n-1]就是前缀在后缀i里出现的次数

HDU - 3336 Count the string (扩展kmp)

It is well known that AekdyCoin is good at string problems as well as number theory problems. When given a string s, we can write down all the non-empty prefixes of this string. For example: s: "abab" The prefixes are: "a", "ab&qu

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 - 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]

acdream 1116 Gao the string!

分析:实际上,我们用next[i]表示 T[i-n] 和T[1-n]的最长公共前缀,那么a[i]=next[i] +next[i+1]...... +next[n];  最长公共前缀(lcp)有3种方法 : 扩展kmp  ,hash 和后缀数组 . 方法一  :扩展kmp //#pragma comment(linker, "/STACK:102400000,102400000") #include<cstdio> #include<algorithm> #in