hdu 5763 Another Meaning
题意是说给你一句话和一个单词,这个单词有两种意思,问这句话可能有几种意思。
不是很好理解题目的意思。
一开始的想法是用kmp跑出所有匹配的位置,然后把匹配出来的区间全部存下来,之后合并互相覆盖的区间,然后去看每个区间可能有几个意思,然后把所有答案乘起来,这样子做很麻烦,而且求每个区间可能有几个意思也不好求,所以这个方法pass了。多校结束之后想了想发现每个匹配都可以分为前面有匹配和他重叠,和没有匹配和他重叠的两种,所以可以dp,先kmp,在kmp过程中每找到一个匹配的位置就去看这个匹配前方有没有另一个匹配和他有重叠部分,如果没有,那么到这个匹配为止的解就是上一次匹配的解乘2,如果有,那么那么现在这个匹配的解是上一个匹配的解的个数加上不和他重叠的最后一个匹配的解的个数的和。其实和背包挺像的。
dp[i]表示到第i个匹配的位置时句意的种数
状态转移方程:dp[i] = dp[i- 1] * 2上一个匹配不和当前重叠
dp[i] = dp[i– 1] + dp[j]上一个匹配和当前重叠,j代表当前匹配前方第一个不和当前重叠的匹配
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 100005;
const long long MOD = 1000000007;
int n;
int pi[N];
long long dp[N];
int pos[N];
char sen[N];
char pat[N];
void CPF(char *str)//处理模式串
{
intk = 0, i;
memset(pi,0, sizeof pi);
for(i = 1; str[i]; i++)
{
while(k > 0 && str[k] != str[i])
k= pi[k];
if(str[k] == str[i])
k++;
pi[i+ 1] = k;
}
}
int KMP(char *t, char *p)
{
intcnt = 0;
intk = 0, i, j;
intlen = strlen(p);
memset(dp,0, sizeof dp);
memset(pos,0, sizeof pos);
pos[cnt]= -1;
dp[cnt]= 1;
CPF(p);
for(i = 0; t[i]; i++)
{
while(k > 0 && p[k] != t[i])
k= pi[k];
if(p[k] == t[i])
k++;
if(k == len)
{
j= 1, cnt++;
if(i - len + 1 <= pos[cnt - 1])
{
while(i - len + 1 <= pos[cnt - j]) j++;
dp[cnt]+= (dp[cnt - j] + dp[cnt - 1]) % MOD;
}
else
dp[cnt]= (dp[cnt - 1] * 2) % MOD;
pos[cnt]= i;
k= pi[k];
}
}
returncnt;
}
int main(void)
{
intT;
intcas = 1;
scanf("%d",&T);
while(T--)
{
cin>> sen;
cin>> pat;
n= KMP(sen, pat);
cout<< "Case #" << cas++ << ": " << dp[n] << endl;
}
return0;
}