#include <iostream>
#include <cstring>
#include <cstdio>
#include <cstdlib>
/**
Title: 【ZOJ3587】Marlon's String——白四爷×KMP 白濑肆の算法完全解读KMP篇 KMP来袭第二弹前缀什么的果然最讨厌了!【1.0%达成!】
Problem: ZOJ3587 Marlon's String
Knowledge Point : KMP算法の深入理解
Pre-Knowledge: KMP算法的理解,可以看之前我发的
http://blog.csdn.net/c0de4fun/article/details/7845625
Thanks To: Lengxiang学长指出%I64d应换为%lld,欠你顿火锅~
Reference:shllhs学长/学姐的一份代码,未找到原始出处,应该为该人所写,代码附后(见Rerfer Code)
Thought:
题意是说,主串中任意两处S[a..b] + S[c..d] = T即可,刚开始脑残把条件看成了a<b<c<d因此日了好久的后缀数组(看见前三个题两个被误认为后缀数组果然我是想多了帝)
其实没有那么复杂,还有人用Extended-KMP做的,在网上搜索教程能看到的就是林希德的那篇文章,不过那篇文章主要介绍的是后缀树,鉴于罗大神的论文中说后缀数组的功能比
后缀树的不逊色,我就懒得看了,本来人就懒。不过貌似后缀树是Trie树加上一些奇奇怪怪的东西(好像就跟AC自动机一样吧加了点其他的东西),用到的时候再说。
言归正传,这道题的思路是这样的:
我们将模式串和主串读入,求一遍KMP,求出模式串pattern的前缀们(【定义】:pattern[0..i] ( i <= 0 < strlen(pattern) )叫做串pattern的一个前缀)
在主串中出现的位置。
然后我们再把模式串和主串(下文pattern指模式串,text指子串,不再特殊说明)都翻转过来,求一遍翻转模式串reversePattern的前缀们在翻转主串reverseText中出现的次数。
那么,ans就等于
int ans = 0;
for(int i = 0 ; i < lenPattern-1 ; i++)
{
ans += originOrder[i] * reverseOrder[len-1 - 1 - i];
}
这么说的原因是,我们假设原来模式串是abcdkkkk,翻转得到kkkkdcba
index: 0 1 2 3 4 5 6 7 ( PatternLength = 8 )
OriginS : a b c d k k k k
ReverseS: k k k k d c b a
那么,原顺序(OriginS)中,i = 2时候的Pattern的前缀pattern[0..2]出现的次数乘以
reversePattern[0..5]出现的次数,才能得到主串中可以以这种方式拼出来模式串的次数。
现在的问题是,前缀的出现次数不会求,怎么破?
这就要用到KMP了好吧我承认我对KMP的理解太水了,以前只是知道快速匹配没想到还可以用来求前缀。
这道题的亮点就在于求模式串的前缀,简直是逼着我们用KMP的说。
屌丝们,还记得当初next数组的作用么。。。
对了(我就是超威蓝猫?)~【next[i]的作用是确保pattern串中
pattern[0..next[i]]与pattern[j-next[i],j]完全相等~】这句话注意,以后会用到。
那么我们在kmp匹配的过程中,如果pattern[j] == text[i]的话,originValue[j](或者reverseValue[j],详见代码,取决于你是否将字符串翻转了)
的值就要+1,因为这代表pattern[0..j]这个前缀在主串text中出现了。
不过,如果只是这么求的话(亦即,遍历text一遍,求出所有前缀出现的次数)还会漏求一些前缀出现的次数。举个例子
Pattern = abcdabcdabc
对应的next为
Pattern = a b c d a b c d a b c
next = -1 -1 -1 -1 0 1 2 3 4 5 6
但是我们注意到,如果在text中匹配到了abcdabc这个前缀,其实pattern的前缀abc也在主串中出现了,这怎么破!?
递归破就可以了,递归是从尾巴向前的。
for(int i = patternLen-1; i >= 0 ; i--)
{
if (next[i] != -1) //在前面出现了某前缀S[0..next[i]]与S[i-next[i]..i]相同
originValue[next[i]] += originValue[i] ; (或者reverseValue[next[i]] += reverseValue[i],具体取决于你是否已经翻转主串和模式串了)
}
这就完爆了,这里如果KMP想不好的话,想起来有一些费劲,结合next[i]函数的意义想一下吧。
如此求完了之后再乘起来就可以了呐。
*/
using namespace std;
const int ORIGIN_ORDER = 1;
const int REVERSE_ORDER = 0;
const int MAX_SIZE = 100005;
long long ans = 0;
char text[MAX_SIZE];
char pattern[MAX_SIZE];
int next[MAX_SIZE];
long long originValue[MAX_SIZE];
long long reverseValue[MAX_SIZE];
void init()
{
memset(text,0,sizeof(text));
memset(pattern,0,sizeof(pattern));
memset(next,-1,sizeof(next));
memset(originValue,0,sizeof(originValue));
memset(reverseValue,0,sizeof(reverseValue));
}
void getNext()
{
int len1 = strlen(pattern);
next[0] = -1;
for(int i = 1 ; i < len1 ; i++)
{
int j = next[i-1];
while( pattern[j+1] != pattern[i] && j>=0 )
j = next[j];
if( pattern[j+1] == pattern[i] )
next[i] = j+1;
else
next[i] = -1;
}
}
bool KMP(int flag)
{
getNext();
int lenp = strlen(pattern);
int lens = strlen(text);
int i = 0 ;
int j = 0 ;
while( i < lens )
{
if( text[i] == pattern[j] )
{
if ( flag == ORIGIN_ORDER )
{
originValue[j]++;
}
if ( flag == REVERSE_ORDER )
{
reverseValue[j]++;
}
i++;
j++;
}
else
{
if( j == 0 ) i++;
else
j = next[j-1]+1;
}
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("B:\\acm\\SummerVacation\\String-I\\C.in","r",stdin);
freopen("B:\\acm\\SummerVacation\\String-I\\C.out","w",stdout);
#endif
int T = 0;
scanf("%d\n",&T);
//while( scanf("%d\n",&T) != EOF )
//{
for(int t = 1 ; t <= T ; t++)
{
init();
gets(text);
gets(pattern);
KMP(ORIGIN_ORDER);
int lenp = strlen(pattern);
int lent = strlen(text);
for(int i = lenp-1 ; i >= 0 ; i-- )
{
if( next[i] > -1 )
{
originValue[next[i]] = originValue[next[i]]+originValue[i];
}
}
/**以下内容全是用来翻转字符串的*/
char tmp;
for(int i = 0 ; i < lenp/2; i++)
{
tmp = pattern[i];
pattern[i] = pattern[lenp-1-i];
pattern[lenp-1-i] = tmp;
}
for(int i = 0 ; i < lent/2; i++)
{
tmp = text[i];
text[i] = text[lent-1-i];
text[lent-1-i]=tmp;
}
memset(next,-1,sizeof(next));
/**翻转+初始化NEXT数组完毕**/
KMP(REVERSE_ORDER);
for(int i = lenp-1 ; i >= 0 ; i--)
{
if( next[i] > -1 )
reverseValue[next[i]] = reverseValue[next[i]]+reverseValue[i];
}
ans = 0;
for(int i = 0 ; i < lenp-1 ; i++)
{
ans = ans + originValue[i]*reverseValue[lenp-1-i-1];
}
printf("%lld\n",ans);
}
//}
#ifndef ONLINE_JUDGE
fclose(stdin);
fclose(stdout);
#endif
return 0;
}
参考代码:(Thx to shllhs)
#include <cstring>
#include <cstdio>
#include <algorithm>
#define MAXN 100010
using namespace std;
int next[MAXN];
char str[MAXN],s[MAXN];
long long a[MAXN],b[MAXN];
void KMP_next(char *s,int *next){
int i,j;
j=next[0]=-1;
for(i=1;s[i]!='\0';i++)
{
next[i]=-1;
while(j>-1&&s[j+1]!=s[i])
j=next[j];
if(s[j+1]==s[i])
{
j++;
next[i]=j;
}
}
}
int main(){
int i,j,t,n,m;
long long ans;
scanf("%d",&t);
while(t--){
scanf("%s%s",str,s);
KMP_next(s,next);
m=strlen(str);
n=strlen(s);
memset(a,0,sizeof(a));
memset(b,0,sizeof(b));
ans=0;
for(i=0,j=-1;i<m;i++){
while(j>-1&&s[j+1]!=str[i])
j=next[j];
if(s[j+1]==str[i]){
j++;
a[j]++;
}
}
for(i=n-1;i>0;i--)
if(next[i]>-1)
a[next[i]]+=a[i];
for(i=0;i+i<m;i++)
swap(str[i],str[m-1-i]);
for(i=0;i+i<n;i++)
swap(s[i],s[n-1-i]);
KMP_next(s,next);
for(i=0,j=-1;i<m;i++){
while(j>-1&&s[j+1]!=str[i])
j=next[j];
if(s[j+1]==str[i]){
j++;
b[j]++;
}
}
for(i=n-1;i>0;i--)
if(next[i]>-1)
b[next[i]]+=b[i];
for(i=0;i+1<n;i++)
ans+=a[i]*b[n-2-i];
printf("%lld\n",ans);
}
return 0;
}