hdu 5763 Another Menning(KMP && DP)
2016-08-05 21:20
211 查看
Another Meaning
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 1231 Accepted Submission(s): 577
Problem Description
As is known to all, in many cases, a word has two meanings. Such as “hehe”, which not only means “hehe”, but also means “excuse me”.
Today, ?? is chating with MeiZi online, MeiZi sends a sentence A to ??. ?? is so smart that he knows the word B in the sentence has two meanings. He wants to know how many >kinds of meanings MeiZi can express.
Input
The first line of the input gives the number of test cases T; T test cases follow.
Each test case contains two strings A and B, A means the sentence MeiZi sends to ??, B means the word B which has two menaings. string only contains lowercase letters.
Limits
T <= 30
|A| <= 100000
|B| <= |A|
Output
For each test case, output one line containing “Case #x: y” (without quotes) , where x is the test case number (starting from 1) and y is the number of the different meaning of this sentence may be. Since this number may be quite large, you should output the answer modulo 1000000007.
Sample Input
4
hehehe
hehe
woquxizaolehehe
woquxizaole
hehehehe
hehe
owoadiuhzgneninougur
iehiehieh
Sample Output
Case #1: 3
Case #2: 2
Case #3: 5
Case #4: 1
Hint
In the first case, “ hehehe” can have 3 meaings: “he”, “he”, “hehehe”.
In the third case, “hehehehe” can have 5 meaings: “hehe”, “he*he”, “hehe”, “**”, “hehehehe”.
Author
FZU
Source
2016 Multi-University Training Contest 4
多校上的一道题,当时一直没有读懂题。比赛完,看了看样例,才发祥是道字符串匹配的题。
KMP算法
说到 字符串匹配的话,就一定绕不开的三个人是:Knuth,Morris,Pratt。对滴,他们就是KMP算法的创始人。
这种算法不太容易理解,网上有很多解释,但读起来都很费劲。这里我说一下,一些自己的看法。
KMP算法的精髓之处,也是最难懂的地方在于 next数组。当模式串与原串失配(不匹配)的时候,他是用来跳转的,很大程度上减少了匹配的次数。先上一下,代码:
void make_next(const p[], next[]){ int q, k; //q:模版字符串下标;k:最大前后缀长度 int m = strlen(p); //模板字符串的长度 next[0] = 0; for(q = 1, k = 0; q < m; q++){ while(k > 0 && p[q] != p[k]) k = next[k-1]; if(p[q] == p[k]) k++; next[q] = k; } }
这里着重说一下,for循环里面的工作:
这是一个一般化的过程,正在计算的next[k], next[1],,,,next[k-1]是已知的。
此时,正在匹配p[q] , p[k];
如果相等呢,直接跳出循环,将k值赋值给next[q];
关键是不相等呢,P[k]已经和P[q]失配了,而且P[q-k] ··· P[q-1]又与P[0] ···P[k-1]相同,看来P[0]···P[k-1]这么长的子串是用不了了,那么我要找个同样也是P[0]打头、P[k-1]结尾的子串即P[0]···P[j-1] (j==next[k-1]),看看它的下一项P[j]是否能和P[q]匹配。这有点递归的意思,体会一下吧。
好啦,有了KMP之后,问题就转换为区间的覆盖问题。这是因为,我们可以通过KMP找到所以匹配开始的点。
简单的DP
状态转移方程为:dp[i] = dp[i+1] + dp[j] (j 是 不与区间 i 覆盖的第一个区间);
注意:有事没事就要取模,否则会爆long long;
上AC代码:
#include <map> #include <set> #include <list> #include <cmath> #include <ctime> #include <deque> #include <queue> #include <stack> #include <string> #include <bitset> #include <cstdio> #include <limits> #include <vector> #include <climits> #include <cstring> #include <cstdlib> #include <fstream> #include <numeric> #include <sstream> #include <iostream> #include <algorithm> #define MEM(a,x) memset(a,x,sizeof(a)) typedef long long ll; typedef unsigned long long ull; using namespace std; const int maxn = 100000 + 100; int t[maxn], next1[100000 + 100]; ll dp[maxn]; void Init(int n) { for(int i = 0; i < n; i++) { t[i] = next1[i] = dp[i] = 0; } } void makenext1(const char P[],int next1[]) { int q,k; int m = strlen(P); next1[0] = 0; for (q = 1,k = 0; q < m; ++q) { while(k > 0 && P[q] != P[k]) k = next1[k-1]; if (P[q] == P[k]) { k++; } next1[q] = k; } } int kmp(const char T[],const char P[],int next1[], int t[]) { int n,m, kase = 0; int i,q; n = strlen(T); m = strlen(P); makenext1(P,next1); for (i = 0,q = 0; i < n; ++i) { while(q > 0 && P[q] != T[i]) q = next1[q-1]; if (P[q] == T[i]) { q++; } if (q == m) { t[kase++] = i - m + 1; //printf("Pattern occurs with shift:%d\n",(i-m+1)); } } return kase; } int main() { // freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int n, q = 0; cin >> n; char T[maxn], P[maxn]; while(n--) { cin >> T >> P; int n1 = strlen(T); int n2 = strlen(P); Init b286 (max(n1, n2) + 1); int len = kmp(T,P,next1, t); dp[t[len-1]] = 2; if(len <= 1) { cout <<"Case #"<< ++q <<": "; cout << len+1 << endl; continue; } for(int i = len-2; i >= 0; i--) { dp[t[i]] += dp[t[i+1]]%1000000007; int k = i+1; int f = 0; while(t[i] + n2 > t[k] && k < len) { k++; if(k == len) f = 1; } if(!f) dp[t[i]] += dp[t[k]]%1000000007; else dp[t[i]] += 1; } cout <<"Case #"<< ++q <<": "; cout << dp[t[0]]%1000000007<< endl; //printf("time=%.3lf\n",(double)clock()/CLOCKS_PER_SEC); } return 0; }
相关文章推荐
- KMP算法的C#实现方法
- JavaScript中数据结构与算法(五):经典KMP算法
- 详解Android应用中屏幕尺寸的获取及dp和px值的转换
- 基于Android中dp和px之间进行转换的实现代码
- Android中dip、dp、sp、pt和px的区别详解
- 简单的四则运算
- 数的奇偶性
- ACMer博客瀑布流分析
- LFC1.0.0 版本发布
- ACM程序设计大赛题目分类
- 2015年acm国内排名
- Android dpi,dip,dp的概念以及屏幕适配
- 计算字符串最后一个单词长度
- Android px、dp、sp之间相互转换
- ACM网址
- 1272 小希的迷宫
- 1272 小希的迷宫
- hdu 1250 大数相加并用数组储存
- HP data protector软件学习1--基本角色与基本工作流程
- HP data protector软件学习2--软件组成与界面介绍