2016 多校 Multi-University Training Contest 4 Another Meaning

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;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值