题目描述
一块花布条,里面有些图案,另有一块直接可用的小饰条,里面也有一些图案。对于给定的花布条和小饰条,计算一下能从花布条中尽可能剪出几块小饰条来呢?
输入
输入中含有一些数据,分别是成对出现的花布条和小饰条,其布条都是用可见ASCII字符表示的,可见的ASCII字符有多少个,布条的花纹也有多少种花样。花纹条和小饰条不会超过1000个字符长。如果遇见#字符,则不再进行工作。
输出
输出能从花纹布中剪出的最多小饰条个数,如果一块都没有,那就老老实实输出0,每个结果之间应换行。
样例输入
abcde a3
aaaaaa aa
#
样例输出
0
3
题解
最近学习了
KMP
字符串算法,心血来潮写一篇博客……
虽说
exKMP
要比
KMP
难一些,但是由于给我讲算法的师姐只给我讲了
exKMP
,所以我只会
exKMP
(因为老师还没有正式讲
QwQ
),貌似
KMP
可以解决的问题
exKMP
都能解决,而
exKMP
能解决的问题
KMP
不一定能解决
QwQ
,所以我就贴
exKMP
的算法啦~
好了,那么什么是
exKMP
呢?
exKMP
问题就是求出模式串
A
与被匹配字符串
例如
A=aab B=aabaaab
B 的后缀起始下标 | 与 A 的最长公共前缀长度 | |
---|---|---|
aabaaab | 3 | |
abaaab | 1 | |
baaab | 0 | |
aaab | 2 | |
aab | 3 | |
ab | 1 | |
b |
exKMP
看似需要
O(n2)
的时间,但是和
KMP
问题一样,
exKMP
问题也可以在
O(n)
的时间内做出来。我们定义两个数组:
nex
:
A
的所有后缀与
ex
:
B
的所有后缀与
我们先看看怎么求出辅助数组
nex
显然,
nex[0]=lena
,
nex[1]
可以
O(n)
暴力求出
然后我们考虑在知道了
nex[1...i−1]
后求出
nex[i]
。
由
nex
的定义得,
nex[j]
表示从第
j
位开始,有连续的
我们在
[1,i)
中找到一个
p
最大的
所以就分成了两种情况:
- 如果 i+nex[i−j]<maxp ,那么 nex[i]=nex[i−j] 。
- 如果
i+nex[i−j]>=maxp
呢?很遗憾,你只能从
maxp
开始暴力做(当然别忘了更新
j
和
maxp )……但是不要沮丧,每一个位置只会被暴力做一次(做完后就被 maxp 覆盖了),所以时间依然是 O(n) !
至此,我们求出了
nex
数组,那怎么求
ex
数组呢?我们可以用类似的步骤重做一次,只需要注意几个细节就基本算是
copy
了……
附上求
nex
代码片
nex[0]=n;//特殊情况
for(int i=1;i<n;i++)
if(A[i]==A[i-1])nex[1]++;
else break;//暴力
maxp=nex[1]+1;po=1;//不说话
for(int i=2;i<n;i++)
{
if(i+nex[i-po]<maxp)nex[i]=nex[i-po];//第一种情况
else nex[i]=max(maxp-i,0);
while(i+nex[i]<n&&A[nex[i]]==A[i+nex[i]])nex[i]++;//第二种情况开始暴力……
if(i+nex[i]>maxp)maxp=i+nex[i],po=i;
}
好了我们回到原题,通过
exKMP
,我们能够求出
ex
数组,如果
ex[i]=n
说明
i
号位置的后缀可以与模式串匹配,那么就可以剪下
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 1000005
char A[N],B[N];//A为模式串 B为匹配串
int nex[N],ex[N],maxp,po,n,m;
void exKMP(char *A,char *B)
{
memset(nex,0,sizeof(nex));
memset(ex,0,sizeof(ex));
n=strlen(A);m=strlen(B);
nex[0]=n;
for(int i=1;i<n;i++)
if(A[i]==A[i-1])nex[1]++;
else break;
maxp=nex[1]+1;po=1;
for(int i=2;i<n;i++)
{
if(i+nex[i-po]<maxp)nex[i]=nex[i-po];
else nex[i]=max(maxp-i,0);
while(i+nex[i]<n&&A[nex[i]]==A[i+nex[i]])nex[i]++;
if(i+nex[i]>maxp)maxp=i+nex[i],po=i;
}
for(int i=0;i<n;i++)
if(A[i]==B[i])ex[0]++;
else break;
maxp=ex[0];po=0;
for(int i=1;i<m;i++)
{
if(i+nex[i-po]<maxp)ex[i]=nex[i-po];
else ex[i]=max(maxp-i,0);
while(ex[i]<n&&i+ex[i]<m&&A[ex[i]]==B[i+ex[i]])ex[i]++;
if(i+ex[i]>maxp)maxp=i+ex[i],po=i;
}
}
int main()
{
for(;;)
{
scanf("%s",B);
if(B[0]=='#')
break;
scanf("%s",A);
exKMP(A,B);
int last=0,sum=0;
for(int i=0;i<m;i++)
if(ex[i]==n&&i>=last)
sum++,last=i+n;
printf("%d\n",sum);
}
}