这两个道题其实差不多,只不过一个允许选的字符有交叉,一个不允许。就是AZAZA中石油2个AZA还是有1个AZA的差别。
HDU - 2087
题目大意:
给定文本串T和匹配串S,问你文本串里有几个匹配串,其中字符不能有交叉。比如AZAZA里面只有一个AZA。
题目思路:
只需要把板子改动一个地方,当我们找到匹配的第一个地方的时候不要跳出来,而是把匹配串重新归零从第一位重新匹配就好了。
附代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#define maxn 1005
using namespace std;
char s[maxn],t[maxn];
int nxt[maxn];
int ans;
int n,m;
void build()
{
int k=0;
nxt[0]=0;
for(int i=1;i<m;i++)
{
while(k&&t[i]!=t[k])
k=nxt[k-1];
if(t[i]==t[k])
k++;
nxt[i]=k;
}
}
void kmp()
{
int j=0;
for(int i=0;i<n;i++)
{
while(j&&s[i]!=t[j])
j=nxt[j-1];
if(s[i]==t[j])
j++;
if(j==m)//前面j++
{
ans++;
j=0;//加上这里即可
}
//return i-m+1;
}
//return -2;
}
int main(void)
{
while(~scanf("%s",s))
{
ans=0;
if(s[0]=='#')
break;
scanf("%s",t);
n=strlen(s);
m=strlen(t);
build();
kmp();
printf("%d\n",ans);
}
return 0;
}
---------------------------------------------------------------我是一个分割线o_o----------------------------------------------------------------------
HDU - 1686
题目大意:
跟上一题差不多,只不过允许有交叉
题目思路:
如果允许有交叉,显然我们在这一位匹配结束了之后,把j回退到 nxt[ j ] 的位置继续比对就好了呀
附代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#define maxn 1000005
using namespace std;
typedef long long ll;
char s[maxn],t[maxn];
ll nxt[maxn];
int n,m;
int ans;
void build()
{
int k=0;
nxt[0]=0;
for(int i=1;i<m;i++)
{
while(k&&t[i]!=t[k])
k=nxt[k-1];
if(t[i]==t[k])
k++;
nxt[i]=k;
}
}
void kmp()
{
int j=0;
for(int i=0;i<n;i++)
{
while(j&&s[i]!=t[j])
j=nxt[j-1];
if(s[i]==t[j])
j++;
if(j==m)//前面j++
{
ans++;
j=nxt[j-1];//这里这里
}
//return i-m+1;
}
//return -2;
}
int main(void)
{
int tt;
scanf("%d",&tt);
while(tt--)
{
ans=0;
memset(nxt,0,sizeof(nxt));
scanf("%s%s",t,s);
n=strlen(s);
m=strlen(t);
build();
kmp();
printf("%d\n",ans);
}
}
/*
3
AZA
AZAZAZA
*/
呼呼