字符串匹配
感觉用字符串hash 可以水过很多字符串匹配的问题,(KMP太头疼了)
实现方法:
计算子串的hash值,在长串中从第一位开始,往后延伸到和子串同样长度,然后算出这段字符的hash值,看是否和子串的hash值相等。
模板
#include<iostream>
#include<cstring>
#include<cstdio>
#define maxn 1000500
using namespace std;
typedef unsigned long long ull;
const int base=133;
ull po[maxn];
ull a[maxn],b[10005];
ull ha[maxn];
ull code(int l,int r)
{
return ha[r]-ha[l-1]*po[r-l+1];
}
int main(void)
{
int t,m,n;
ull hb;
scanf("%d",&t);
po[0]=1;
for(int i=1;i<maxn;i++)
{
po[i]=po[i-1]*base;//记得乘上相应的po值
}
while(t--)
{
memset(ha,0,sizeof(ha));
scanf("%d%d",&n,&m);
ha[0]=0;//如果子串的长度不是固定的不能把ha[]的值设为0,因为掩盖了长度信息
for(int i=1;i<=n;i++)
{
scanf("%lu",&a[i]);
ha[i]=ha[i-1]*base+a[i];
}
hb=0;
for(int i=1;i<=m;i++)
{
scanf("%lu",&b[i]);
hb=hb*base+b[i];//算出b的哈希值
}
int t=-1;
for(int i=1;i<=n-m+1;i++)
{
if(code(i,i+m-1)==hb)
{
t=i;
break;
}
}
printf("%d\n",t);
}
return 0;
}
与DP结合
例题:题目链接
题目大意:
上面串A是原文,下面的子串B表示这串字符有两个意思,问你原文可能会有多少个意思。
题目思路:
用dp[i]表示以i结尾的原文会有多少个意思,那么当以第i位结束的长度为lb=len(B)的字符串和B匹配的话,
dp[i]=dp[i-lb]+dp[i-1];①
否则dp[i]=dp[i-1]②
比如A为hehehe,B为hehe
显然我们应该从A的第四位开始循环判断,往前数4个数字(B的长度),如果和B匹配就执行①,否则执行②
至于判断是否匹配,可以用字符串哈希,相当快~
代码:
#include<iostream>
#include<cstring>
#include<string>
#include<cstdio>
#define maxn 100005
#define mod 1000000007
using namespace std;
typedef unsigned long long ull;
const int base=133;
ull po[maxn];
ull dp[maxn];
ull ha[maxn];
char a[maxn],b[maxn];
ull code(int l,int r)
{
return ha[r]-ha[l-1]*po[r-l+1];
}
int main(void)
{
int t;
scanf("%d",&t);
po[0]=1;
for(int i=1;i<maxn;i++)
{
po[i]=po[i-1]*base;
}
int kase=0;
while(t--)
{
memset(dp,0,sizeof(dp));
memset(ha,0,sizeof(ha));
//
scanf("%s",a+1);
scanf("%s",b+1);
int la=strlen(a+1);int lb=strlen(b+1);
ull hb=0;
for(int i=1;i<=lb;i++)
{
hb=hb*base+b[i]-'a';//计算出b的哈希值
}
//开始处理a
ha[0]=0;
for(int i=1;i<=la;i++)
ha[i]=ha[i-1]*base+a[i]-'a';
//
for(int i=0;i<lb;i++)
dp[i]=1;
for(int i=lb;i<=la;i++)
{
ull hs=code(i-lb+1,i);
if(hs!=hb)
dp[i]=dp[i-1]%mod;
else
dp[i]=(dp[i-lb]%mod+dp[i-1]%mod)%mod;
}
printf("Case #%d: %lu\n",++kase,dp[la]);
}
return 0;
}
呼呼