bzoj1009 [HNOI2008]GT考试
2015-06-20 20:39
399 查看
1009: [HNOI2008]GT考试
Time Limit: 1 Sec Memory Limit: 162 MBSubmit: 2120 Solved: 1304
Description
阿申准备报名参加GT考试,准考证号为N位数X1X2....Xn(0<=Xi<=9),他不希望准考证号上出现不吉利的数字。他的不吉利数学A1A2...Am(0<=Ai<=9)有M位,不出现是指X1X2...Xn中没有恰好一段等于A1A2...Am. A1和X1可以为0Input
第一行输入N,M,K.接下来一行输入M位的数。 100%数据N<=10^9,M<=20,K<=1000 40%数据N<=1000 10%数据N<=6Output
阿申想知道不出现不吉利数字的号码有多少种,输出模K取余的结果.Sample Input
4 3 100111
Sample Output
81HINT
Source
大意:求有多少n位的字符串 不包含给出的字符串分析:n那么大,那肯定是矩阵乘法快速幂了。
可以用KMP,但自从AC自动机出现,KMP就成了时代的眼泪(#题外话)
所以我不会用KMP,所以我用了AC自动机
其实无论是一个限制串还是多个限制串(前提是总字符数量不要太多),原理都是一样的
下面介绍一下AC自动机
(我的介绍比较简略,如果看还是结合别人的介绍比较好)
1 2 3 4 5 6 7 8 9 10 11
原始串 A : a b c d a b c d a b d
要寻找的串 B :a b c d a b d
比如我们现在匹配,比如第 7 位不同,我们肯定不会从B串的第一位从新开始寻找,而是从第3位开始匹配,因为第 1 - 2 个字符与第5 - 6个字符相同(都是a b ),所以与KMP类似,我们在建设AC自动机时,要对每一位(假设第 x 位)的字符都找一个点,使得第 1 - i 位的字符与 第 x - i + 1 - x位的字符相同,并是的这样的与开头一段相同的串最长(即 i 最大),这样来解决匹配失败的问题,这个东西我们用Next 来记录(即匹配失败后,从哪一位开始匹配),而AC自动机就是在字母树的基础上添加Next的用法,如图所示。
#include <cstdio> #include <cstring> #include <cstdlib> #include <cmath> #include <deque> #include <vector> #include <queue> #include <iostream> #include <algorithm> #include <map> #include <set> #include <ctime> using namespace std; typedef long long LL; typedef double DB; #define For(i, s, t) for(int i = (s); i <= (t); i++) #define Ford(i, s, t) for(int i = (s); i >= (t); i--) #define MIT (2147483647) #define INF (1000000001) #define MLL (1000000000000000001LL) #define sz(x) ((int) (x).size()) #define clr(x, y) memset(x, y, sizeof(x)) #define puf push_front #define pub push_back #define pof pop_front #define pob pop_back #define ft first #define sd second #define mk make_pair inline void SetIO(string Name) { string Input = Name+".in", Output = Name+".out"; freopen(Input.c_str(), "r", stdin), freopen(Output.c_str(), "w", stdout); } const int N = 30, M = 20; int n, m, Mod; int Tot; struct Matrix { int M ; Matrix() { clr(M, 0); } inline Matrix operator *(Matrix &A) { Matrix Ret; For(i, 0, Tot) For(j, 0, Tot) For(k, 0, Tot) { Ret.M[i][j] += M[i][k]*A.M[k][j]; Ret.M[i][j] %= Mod; } return Ret; } } Transfer; struct Node { int Child[M], Next; bool Danger; #define Child(x, y) Tr[x].Child[y] #define Next(x) Tr[x].Next #define Danger(x) Tr[x].Danger } Tr ; string S; queue<int> Que; inline void Input() { scanf("%d%d%d", &n, &m, &Mod); cin>>S; } inline int Find(int x, int y) { while(x && !Child(x, y)) x = Next(x); return Child(x, y); } inline void Build() { int Now = 0; For(i, 0, m-1) { int x = S[i]-'0'; Child(Now, x) = ++Tot; Danger(Tot) = 0; Now = Tot; } Danger(Tot) = 1; Next(0) = 0; For(i, 0, 9) Next(Child(0, i)) = 0; For(i, 0, 9) if(Child(0, i)) Que.push(Child(0, i)); while(sz(Que)) { int x = Que.front(); Que.pop(); for(int Tab = Next(x); Tab && !Danger(x); Tab = Next(Tab)) Danger(x) |= Danger(Tab); For(i, 0, 9) if(Child(x, i)) Que.push(Child(x, i)); For(i, 0, 9) if(Child(x, i)) Next(Child(x, i)) = Find(Next(x), i); else Child(x, i) = Find(Next(x), i); } For(i, 0, Tot) { if(Danger(i)) continue; For(j, 0, 9) { if(Danger(Child(i, j))) continue; Transfer.M[i][Child(i, j)]++; } } } inline Matrix Power(Matrix &Basic, int Tim) { Matrix Ret; For(i, 0, Tot) Ret.M[i][i] = 1; while(Tim) { if(Tim&1) Ret = Ret*Basic; Basic = Basic*Basic, Tim >>= 1; } return Ret; } inline void Solve() { Build(); Matrix Ret; Ret = Power(Transfer, n); int Ans = 0; For(i, 0, Tot) if(!Danger(i)) Ans += Ret.M[0][i]; Ans %= Mod; printf("%d\n", Ans); } int main() { SetIO("1009"); Input(); Solve(); return 0; }
View Code
题外话:
为什么说KMP是时代的眼泪了呢?
其实这样说是不太准确的,KMP还是可以求最长重复子串的(子串之间不允许重叠),其他问题可以用其他方法解决
如果求最长重复子串(子串之间允许重叠),则用后缀数组,
至于其他,则大多可以用AC自动机代替
(当然,还有少数功能这里没有提到)
而后缀数组、AC自动机属于必学,且变化形式较少,代码较模版化,思考较简单,
KMP则有时需要大神的思维以及一点点小火花,
所以KMP还是不用专精,略学即可
(其实AC自动机跟KMP原理是一样的,我只不过实在胡说八道罢了)
相关文章推荐
- LeetCode之“散列表”:Contains Duplicate && Contains Duplicate II
- ajax成功跨域_自己写的
- 1.3 在集群节点上使用Red Hat 开关脚本
- MATLAB中Legend的一些控制方法
- Jump Game - LeetCode 55
- CentOS 6.4配置resin+apache
- JAVA连接oracle数据库代码
- leetcode - Container With Most Water
- 【从头到尾 - 餐饮管理系统】 - (四)
- 《人,绩效和职业道德》读后感
- 1.2 用init启动服务
- Android两种常见错误(ANR和FC)解决办法
- Groovy入门教程
- C++文件读写详解(ofstream,ifstream,fstream)
- 新手学cocos2dx,centos7下的安装过程
- Oracle 11g client安装和配置。
- DCT算法可以说是有损压缩的第一步,多用在视频压缩方面。
- 1.1 如何启动集群服务?
- pppoe抓包流程和拨号流程
- java方面中,参数后面跟三个点(...)的含义---例如String...