P5256 [JSOI2013] 编程作业 个人题解
说句闲话:题解管理员大大的审核速度天下第一
题目大意:
给定两个字符串,要求第二个字符串在第一个字符串内类似的出现过多少次
Solution:
这道题是字符串查找的强化版,其实搞懂了那道题这道题就很简单了。
我们首先来看什么情况下两个字符串类似,其实要满足三个条件:
- 两个字符串 \(s1,s2\) 长度相等。
- 当 \(s1_{i}\) 为大写字母时,\(s2_{i}\) 也必须为相同的大写字母。
- 当 \(s1_{i}=s1_{j}\) 且这两个均为小写字母时必须保证 \(s2_{i}=s2{j}\) 同时也得是小写字母(不过不一定需要相等)。
前两个条件好办,第三个条件就有些许困难了,我们发现其与小写字母是什么无关,而是与小写字母之间的位置关系有关,当位置关系一致时就可以匹配上,那么怎么求位置关系呢,我们用两个数组来处理,一个 \(pre\) 数组来表示上一个相同的字符出现的位置,一个 \(a\) 数组来表示现在这一个字符到上一个相同字符的距离,然后我们就把问题转变为用 \(s2\) 的 \(a\) 数组去匹配 \(s1\) 的 \(a\) 数组能匹配上多少次。但是同那道题一样,如果把第一次出现的数字的 \(a\) 数组设置为 \(0\),就会导致匹配不上,最简单解决这类问题的方法是可以为它们设置一个通配符,即什么也可以匹配的上,在这里我用其第一次出现的位置作为其通配符(其实也可以用设置成 \(-1\) 来处理)。
然后我们看大写字母,这是与那道题不一样的地方,因为如果我们直接存大写字母进去的话会影响 \(a\) 数组,导致小写字母匹配时出错,不过我们可以把大写字母赋成负值,这样在处理时就不会出问题了(原因在下面)
然后变成的这个问题就需要用到 KMP 算法(不懂得可以看看这篇题解的前半段)了,一个用于处理一个字符串在另一个字符串内出现的次数。不过在比较是否相等时要注意不能与通配符比较是否相等,其实只需要比较当前的 \(a\) 数组是否大于当前匹配的长度,我们可以写一个 compare 函数来比较:
inline bool compare(int a,int b,int len){
return (a==b) || (a>len && b>len);
}
那么为什么把大写字母设为负值就不会出问题呢,我们发现大写字母不会与小写字母去比较,且不会进入判断是否有通配符的语句,它只是在跑普通的 KMP 罢了,所以通过赋负值的方法我们解决了大小写区分的问题了。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+5;
inline int read(){
int x=0,f=1;
char c=getchar();
while(c<'0' || c>'9'){
if(c=='-')
f=-1;
c=getchar();
}
while(c>='0' && c<='9'){
x=x*10+c-'0';
c=getchar();
}
return x*f;
}
int Q=read(),nxt[N],pre[N],aa[N],bb[N];
char a[N],b[N];
inline bool compare(int a,int b,int len){//比较是否相等同时判断有无通配符
return (a==b) || (a>len && b>len);
}
int main(){
while(Q--){
int ans=0;
scanf("%s%s",a+1,b+1);
int n=strlen(a+1);
int m=strlen(b+1);
memset(pre,0,sizeof(pre));
for(int i=1;i<=n;i++){
if(a[i]>='A' && a[i]<='Z')//如果是大写字母直接赋负值
aa[i]=-(a[i]-'A'+1);
else{
aa[i]=i-pre[a[i]-'a'+1];
pre[a[i]-'a'+1]=i;
}
}
memset(pre,0,sizeof(pre));
for(int i=1;i<=m;i++){
if(b[i]>='A' && b[i]<='Z')
bb[i]=-(b[i]-'A'+1);
else{
bb[i]=i-pre[b[i]-'a'+1];
pre[b[i]-'a'+1]=i;
}
}
nxt[1]=0;
for(int i=2,j=0;i<=m;i++){
while(j>0 && !compare(bb[j+1],bb[i],j))
j=nxt[j];
if(compare(bb[j+1],bb[i],j))
j++;
nxt[i]=j;
}
for(int i=1,j=0;i<=n;i++){
while(j>0 && !compare(bb[j+1],aa[i],j))
j=nxt[j];
if(compare(bb[j+1],aa[i],j))
j++;
if(j==m){
ans++;
j=nxt[j];
}
}
printf("%d\n",ans);
}
return 0;
}
再说一句,这道题有加强版,可以当作双倍经验做做。

浙公网安备 33010602011771号