Trie、KMP、AC自动机小结
2014-02-05 21:58
393 查看
最近做了不字符串的题,做下小结吧~
首先是Trie(也叫前缀树),Trie的结构并不难理解,Trie是个树形结构,它的每条边对应一个字符,每个节点对应一个字符串的前缀(根节点对应空串),将给定的字符串建立起一棵Trie以后,查找一个串的复杂度就是O(L)的。在对应的节点处可以做各种标记,根据不同的情况有不同的用法,代码也很好写,不过Trie的空间要求比较大……下面贴几道题。。。。
hdu 1671 Phone List
给出一堆串,问是否有的串是另一个串的前缀。。比较简单的题吧,在每个单词结尾的节点打个标记,然后插入的时候判断是否到达某一个节点的末尾,当插入的单词到结尾时判断是否还能走就ok了
代码:
UVALive 3942 Remember the Word(Trie+DP)
给出n个字典串和一个长串,把这个字符串分解成若干个单词连接,有多少种方法。
不错的题,先建立字典树,然后DP搞之。
题解:
http://blog.csdn.net/qian99/article/details/18730455
UVA 11732 strcmp() Anyone
给出一个strcmp()函数,再给出n个字符串,问这n个字符串使用给出的函数两两比较,总共需要比较的次数。也是不错的题,把字符串边插入边计算即可,做了这题会让你对Trie的结构理解的更深一点。比较坑爹的是时限比较严,用了左儿子右兄弟表示法才过掉……
题解:
http://blog.csdn.net/qian99/article/details/18735485
UVA 11488 Hyper Prefix Sets(Trie)
给出n个串,问这n个串的公共前缀乘字符个数的最大值。这题跟上一题差不多,也是边插入边算就行了。
题解:
http://blog.csdn.net/qian99/article/details/18736843
接下来就是KMP了,KMP网上资料非常多,也就不多说了(省得丢人啊)……
hdu 2087 剪花布条
给出两个串A和B,问B在A中的匹配次数(不重叠)。
比较简单的KMP应用吧,构造完next数组后去匹配,和普通匹配不同的是,匹配成功以后不能退出,而是答案+1,并且将状态置0,接着匹配。
代码:
hdu 3336 Count the string
给出一个字符串,问它的所有前缀和它匹配的次数。其实KMP求出的next函数是一个状态转移的函数,当发生转移时,如果转移到0,此时是空串,如果不是,那么说明当前状态和前面是有匹配的。
题解:
/article/2708753.html
hdu 3746 Cyclic Nacklace
给出一个串,问最少在串末尾添加几个字符,才能让串中出现循环。KMP的性质,在题解里应该说的比较明白~
题解:
http://blog.csdn.net/qian99/article/details/18770957
hdu 1358 Period
给出一个串,求这个串每个前缀能否形成循环,并输出位置和循环节。明白上一题,这题就非常好做了~
题解:
http://blog.csdn.net/qian99/article/details/18771803
hdu 2594 Simpsons’ Hidden Talents
给出两个串S1和S2,求S1的一个最长前缀,并且满足它是S2的后缀。蛮有意思的一道题,用S1匹配S2,一直匹配到结尾,返回成功匹配的长度。
题解:
http://blog.csdn.net/qian99/article/details/18772529
KMP告一段落,下面是AC自动机~AC自动机可以看成是Trie和KMP的结合……不过这么说也不太准确,我的理解是KMP和AC自动机都是一种有限自动机,有限自动机的概念可以看一下算导,理解了这个,KMP和AC自动机都不难理解了。其实KMP和AC自动机都建立起了一个状态转移图,只不过KMP是一条链。AC自动机建立起来以后对于每个字符都会转移到下一个状态,而在一些状态下保存信息(比如在单词结尾做标记),就可以处理多个串和一个串匹配的问题。另外由于每次添加一个字符都会发生状态转移,所以总会跟着DP一起出现……
下面的题基本都是从kuangbin巨巨那里淘的……
UVALive 4670 Dominating Patterns
给出n个串,问文本串A中哪些串出现的次数最多。基本上市模板题,需要注意的是给出的串有可能重复,这里用map搞一下就好了。
题解:
http://blog.csdn.net/qian99/article/details/18793249
UVA 11468 Substring
给出文章中可以出现的字符以及出现的概率,在给出n个模板串,求随机生成长度为L的串并且不包含任何模板串的概率。
AC自动机+记忆化搜索……dp[u][L]表示当前在状态u,字符串还需要添加L个字符并且不包含任何模板串的概率……AC自动机建立的时候在所有模板串的末尾打标记,表示该状态不能到达。然后直接记忆化搜索即可。
代码:
poj 1625 Censored!(AC自动机+DP+高精)
给出n个模式串,问生成长度为M并且不包含任何模式串的方案数。做法和上一题差不多,只不过这题要写个高精。
题解:
http://blog.csdn.net/qian99/article/details/18900867
hdu 2825 Wireless Password(AC自动机+状压DP)
给出m个串,现在要生成一个长度为n的串,并且这个串至少包含k个给出的串,问生成串的方案数。思路都形似,把k用二进制表示,然后一个三维Dp搞之。
题解:
http://blog.csdn.net/qian99/article/details/18903607
poj 2778 DNA Sequence(AC自动机+矩阵快速幂)
这题和poj 1625意思一样,但是做法不同,原因是要生成的串的长度太长……这里的做法就是把所有的状态可以发生转移的情况生成一个矩阵,然后用矩阵快速幂做。。。
代码:
HDU 2296 Ring(AC自动机+DP)
给出m个串,每个串都有一个价值,现在要构造一个长度小于等于n的串,使得其包含的价值最大。这题思路挺好想的,但是要输出路径,最开始记录的字符,怎么写都不太对,后来弃疗了,直接每个状态记录了个串。
题解:
http://blog.csdn.net/qian99/article/details/18908975
hdu 2457 DNA repair(AC自动机+DP)
给出n个病毒串,然后给出一个长串 ,可以任意修改这个串,要求这个串不包含病毒串,问最小修改次数。构建完AC自动机以后也是简单dp~
题解:
http://blog.csdn.net/qian99/article/details/18909137
ZOJ 3228 Searching the String
给出n个串,每次询问这些串在A中的匹配数,有些串可以覆盖,有些串不可以覆盖。这里记录一下串的长度就行了,不是很难。
题解:
http://blog.csdn.net/qian99/article/details/18910643
hdu 3341 Lost's revenge(AC自动机+DP)
给出n个串,重排串A,使得n个串在A中匹配数最多。状态转移不难想,只是状态不是很好表示,正常表示一定会爆内存,其实表示方法挺多的,随便搞搞就行了~
题解:
http://blog.csdn.net/qian99/article/details/18923495
hdu 3247 Resource Archiver(AC自动机+状压DP)
给出m个病毒串,现在要把n个串合并,并且使得合并后的串中不包含病毒串。做得非常心酸,调了半天bug发现有个地方没赋初值,唉,又犯2了……
题解:
http://blog.csdn.net/qian99/article/details/18939153
ZOJ 3494 BCD Code(AC自动机+数位DP)
给出一些禁止串,问从A~B的数字用BCD码表示,并且不包含禁止串的数有多少。AC自动机真是和什么都能结合啊,好神奇~
题解:
http://blog.csdn.net/qian99/article/details/18940379
差不多就到这里了~最近做字符串有些恶心了,该做点儿别的调节调节心情了~以后再做或许还会发?
首先是Trie(也叫前缀树),Trie的结构并不难理解,Trie是个树形结构,它的每条边对应一个字符,每个节点对应一个字符串的前缀(根节点对应空串),将给定的字符串建立起一棵Trie以后,查找一个串的复杂度就是O(L)的。在对应的节点处可以做各种标记,根据不同的情况有不同的用法,代码也很好写,不过Trie的空间要求比较大……下面贴几道题。。。。
hdu 1671 Phone List
给出一堆串,问是否有的串是另一个串的前缀。。比较简单的题吧,在每个单词结尾的节点打个标记,然后插入的时候判断是否到达某一个节点的末尾,当插入的单词到结尾时判断是否还能走就ok了
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<algorithm> #include<map> #include<queue> #include<set> #include<stack> #include<cmath> #include<vector> #define inf 0x3f3f3f3f #define Inf 0x3FFFFFFFFFFFFFFFLL #define eps 1e-9 #define pi acos(-1.0) using namespace std; typedef long long ll; const int maxn=100000+10; int ch[maxn][10],val[maxn],size; char str[20]; void Init() { memset(ch[0],0,sizeof(ch[0])); val[0]=0;size=0; } bool Insert(const char *s) { int u=0,n=strlen(s); for(int i=0;i<n;++i) { int c=str[i]-'0'; if(!ch[u][c]) { ch[u][c]=++size; memset(ch[size],0,sizeof(ch[size])); val[size]=0; } else if(i==n-1) return false; u=ch[u][c]; if(val[u]) return false; } val[u]=1; return true; } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int t; scanf("%d",&t); while(t--) { int n; bool flag=true; Init(); scanf("%d",&n); for(int i=0;i<n;++i) { scanf("%s",str); flag=flag&&Insert(str); } if(flag) puts("YES"); else puts("NO"); } return 0; }
UVALive 3942 Remember the Word(Trie+DP)
给出n个字典串和一个长串,把这个字符串分解成若干个单词连接,有多少种方法。
不错的题,先建立字典树,然后DP搞之。
题解:
http://blog.csdn.net/qian99/article/details/18730455
UVA 11732 strcmp() Anyone
给出一个strcmp()函数,再给出n个字符串,问这n个字符串使用给出的函数两两比较,总共需要比较的次数。也是不错的题,把字符串边插入边计算即可,做了这题会让你对Trie的结构理解的更深一点。比较坑爹的是时限比较严,用了左儿子右兄弟表示法才过掉……
题解:
http://blog.csdn.net/qian99/article/details/18735485
UVA 11488 Hyper Prefix Sets(Trie)
给出n个串,问这n个串的公共前缀乘字符个数的最大值。这题跟上一题差不多,也是边插入边算就行了。
题解:
http://blog.csdn.net/qian99/article/details/18736843
接下来就是KMP了,KMP网上资料非常多,也就不多说了(省得丢人啊)……
hdu 2087 剪花布条
给出两个串A和B,问B在A中的匹配次数(不重叠)。
比较简单的KMP应用吧,构造完next数组后去匹配,和普通匹配不同的是,匹配成功以后不能退出,而是答案+1,并且将状态置0,接着匹配。
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<algorithm> #include<map> #include<queue> #include<set> #include<stack> #include<cmath> #include<vector> #define inf 0x3f3f3f3f #define Inf 0x3FFFFFFFFFFFFFFFLL #define eps 1e-9 #define pi acos(-1.0) using namespace std; typedef long long ll; const int maxn=1000+10; char s1[maxn],s2[maxn]; int next[maxn],ans; void Kmp() { int n=strlen(s1); int m=strlen(s2); if(m>n) return ; int j=0; for(int i=0;i<n;++i) { while(j&&s1[i]!=s2[j]) j=next[j]; if(s1[i]==s2[j]) j++; if(j==m) {ans++;j=0;} } } void getnext() { int n=strlen(s2); next[0]=next[1]=0; for(int i=1;i<n;++i) { int j=next[i]; while(j&&s2[i]!=s2[j]) j=next[j]; next[i+1]=(s2[i]==s2[j])?j+1:0; } } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); while(~scanf("%s",s1)) { if(s1[0]=='#') break; scanf("%s",s2); ans=0; getnext(); Kmp(); printf("%d\n",ans); } return 0; }
hdu 3336 Count the string
给出一个字符串,问它的所有前缀和它匹配的次数。其实KMP求出的next函数是一个状态转移的函数,当发生转移时,如果转移到0,此时是空串,如果不是,那么说明当前状态和前面是有匹配的。
题解:
/article/2708753.html
hdu 3746 Cyclic Nacklace
给出一个串,问最少在串末尾添加几个字符,才能让串中出现循环。KMP的性质,在题解里应该说的比较明白~
题解:
http://blog.csdn.net/qian99/article/details/18770957
hdu 1358 Period
给出一个串,求这个串每个前缀能否形成循环,并输出位置和循环节。明白上一题,这题就非常好做了~
题解:
http://blog.csdn.net/qian99/article/details/18771803
hdu 2594 Simpsons’ Hidden Talents
给出两个串S1和S2,求S1的一个最长前缀,并且满足它是S2的后缀。蛮有意思的一道题,用S1匹配S2,一直匹配到结尾,返回成功匹配的长度。
题解:
http://blog.csdn.net/qian99/article/details/18772529
KMP告一段落,下面是AC自动机~AC自动机可以看成是Trie和KMP的结合……不过这么说也不太准确,我的理解是KMP和AC自动机都是一种有限自动机,有限自动机的概念可以看一下算导,理解了这个,KMP和AC自动机都不难理解了。其实KMP和AC自动机都建立起了一个状态转移图,只不过KMP是一条链。AC自动机建立起来以后对于每个字符都会转移到下一个状态,而在一些状态下保存信息(比如在单词结尾做标记),就可以处理多个串和一个串匹配的问题。另外由于每次添加一个字符都会发生状态转移,所以总会跟着DP一起出现……
下面的题基本都是从kuangbin巨巨那里淘的……
UVALive 4670 Dominating Patterns
给出n个串,问文本串A中哪些串出现的次数最多。基本上市模板题,需要注意的是给出的串有可能重复,这里用map搞一下就好了。
题解:
http://blog.csdn.net/qian99/article/details/18793249
UVA 11468 Substring
给出文章中可以出现的字符以及出现的概率,在给出n个模板串,求随机生成长度为L的串并且不包含任何模板串的概率。
AC自动机+记忆化搜索……dp[u][L]表示当前在状态u,字符串还需要添加L个字符并且不包含任何模板串的概率……AC自动机建立的时候在所有模板串的末尾打标记,表示该状态不能到达。然后直接记忆化搜索即可。
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<algorithm> #include<map> #include<queue> #include<set> #include<stack> #include<cmath> #include<vector> #define inf 0x3f3f3f3f #define Inf 0x3FFFFFFFFFFFFFFFLL #define eps 1e-9 #define pi acos(-1.0) using namespace std; typedef long long ll; const int maxn=2000+10; double p[110],dp[maxn][110]; bool vis[maxn][110]; int ch[maxn][63],val[maxn],next[maxn],size,n; int idx(char c) { if(c>='0'&&c<='9') return c-'0'; if(c>='a'&&c<='z') return c-'a'+10; return c-'A'+10+26; } void Init() { memset(vis,0,sizeof(vis)); memset(ch[0],0,sizeof(ch[0])); memset(next,0,sizeof(next)); memset(val,0,sizeof(val)); memset(p,0,sizeof(p)); size=0; } void insert(const char *s) { int u=0,len=strlen(s); for(int i=0;i<len;++i) { int c=idx(s[i]); if(!ch[u][c]) { ch[u][c]=++size; memset(ch[size],0,sizeof(ch[size])); val[size]=0; } u=ch[u][c]; } val[u]=1; } void build() { queue<int>q; for(int i=0;i<62;++i) { if(ch[0][i]) q.push(ch[0][i]); } while(!q.empty()) { int u=q.front();q.pop(); for(int i=0;i<62;++i) { int v=ch[u][i]; if(!v) {ch[u][i]=ch[next[u]][i];continue;} q.push(v); int j=next[u]; while(j&&!ch[j][i]) j=next[j]; next[v]=ch[j][i]; val[v]|=val[next[v]]; } } } double f(int u,int L) { if(L==0) return 1.0; if(vis[u][L]) return dp[u][L]; vis[u][L]=true; dp[u][L]=0; for(int i=0;i<62;++i) { if(!val[ch[u][i]]) dp[u][L]+=p[i]*f(ch[u][i],L-1); } return dp[u][L]; } char str[110]; int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int t,tcase=0; scanf("%d",&t); while(t--) { tcase++; Init(); int K; scanf("%d",&K); for(int i=0;i<K;++i) { scanf("%s",str); insert(str); } scanf("%d",&n); char c[3]; for(int i=0;i<n;++i) { scanf("%s",c); scanf("%lf",&p[idx(c[0])]); } build(); int L; scanf("%d",&L); double ans=f(0,L); printf("Case #%d: %lf\n",tcase,ans); } return 0; }
poj 1625 Censored!(AC自动机+DP+高精)
给出n个模式串,问生成长度为M并且不包含任何模式串的方案数。做法和上一题差不多,只不过这题要写个高精。
题解:
http://blog.csdn.net/qian99/article/details/18900867
hdu 2825 Wireless Password(AC自动机+状压DP)
给出m个串,现在要生成一个长度为n的串,并且这个串至少包含k个给出的串,问生成串的方案数。思路都形似,把k用二进制表示,然后一个三维Dp搞之。
题解:
http://blog.csdn.net/qian99/article/details/18903607
poj 2778 DNA Sequence(AC自动机+矩阵快速幂)
这题和poj 1625意思一样,但是做法不同,原因是要生成的串的长度太长……这里的做法就是把所有的状态可以发生转移的情况生成一个矩阵,然后用矩阵快速幂做。。。
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<algorithm> #include<map> #include<queue> #include<set> #include<stack> #include<cmath> #include<vector> #define inf 0x3f3f3f3f #define Inf 0x3FFFFFFFFFFFFFFFLL #define eps 1e-9 #define pi acos(-1.0) using namespace std; typedef long long ll; const int maxn=200+10; const int mod=100000; int ch[maxn][4],next[maxn],flag[maxn],size; int indx[maxn]; void Init() { memset(ch[0],0,sizeof(ch[0])); memset(next,0,sizeof(next)); indx['A']=0;indx['C']=1;indx['T']=2;indx['G']=3; size=0; } void Insert(const char * s) { int u=0,n=strlen(s); for(int i=0;i<n;++i) { int c=indx[s[i]]; if(!ch[u][c]) { ch[u][c]=++size; memset(ch[size],0,sizeof(ch[size])); flag[size]=0; } u=ch[u][c]; } flag[u]=1; } void build() { queue<int>q; for(int i=0;i<4;++i) if(ch[0][i]) q.push(ch[0][i]); int r,u,v; while(!q.empty()) { r=q.front();q.pop(); for(int c=0;c<4;++c) { u=ch[r][c]; if(!u) {ch[r][c]=ch[next[r]][c];continue;} q.push(u); v=next[r]; while(v&&!ch[v][c]) v=next[v]; next[u]=ch[v][c]; flag[u]|=flag[next[u]]; } } } char str[55]; ll matrix[maxn][maxn],res[maxn][maxn],tmp[maxn][maxn]; void mul(ll a[maxn][maxn],ll b[maxn][maxn]) { for(int i=0;i<=size;++i) for(int j=0;j<=size;++j) { tmp[i][j]=0; for(int k=0;k<=size;++k) { tmp[i][j]+=a[i][k]*b[k][j]; tmp[i][j]%=mod; } } for(int i=0;i<=size;++i) for(int j=0;j<=size;++j) b[i][j]=tmp[i][j]; } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int m,n; Init(); scanf("%d%d",&m,&n); for(int i=0;i<m;++i) { scanf("%s",str); Insert(str); } build(); memset(matrix,0,sizeof(matrix)); memset(res,0,sizeof(res)); for(int i=0;i<=size;++i) { if(flag[i]) continue; for(int c=0;c<4;++c) if(!flag[ch[i][c]]) matrix[i][ch[i][c]]++; } for(int i=0;i<=size;++i) res[i][i]=1; while(n) { if(n&1) mul(matrix,res); mul(matrix,matrix); n>>=1; } int ans=0; for(int i=0;i<=size;++i) ans=(ans+res[0][i])%mod; printf("%d\n",ans); return 0; }
HDU 2296 Ring(AC自动机+DP)
给出m个串,每个串都有一个价值,现在要构造一个长度小于等于n的串,使得其包含的价值最大。这题思路挺好想的,但是要输出路径,最开始记录的字符,怎么写都不太对,后来弃疗了,直接每个状态记录了个串。
题解:
http://blog.csdn.net/qian99/article/details/18908975
hdu 2457 DNA repair(AC自动机+DP)
给出n个病毒串,然后给出一个长串 ,可以任意修改这个串,要求这个串不包含病毒串,问最小修改次数。构建完AC自动机以后也是简单dp~
题解:
http://blog.csdn.net/qian99/article/details/18909137
ZOJ 3228 Searching the String
给出n个串,每次询问这些串在A中的匹配数,有些串可以覆盖,有些串不可以覆盖。这里记录一下串的长度就行了,不是很难。
题解:
http://blog.csdn.net/qian99/article/details/18910643
hdu 3341 Lost's revenge(AC自动机+DP)
给出n个串,重排串A,使得n个串在A中匹配数最多。状态转移不难想,只是状态不是很好表示,正常表示一定会爆内存,其实表示方法挺多的,随便搞搞就行了~
题解:
http://blog.csdn.net/qian99/article/details/18923495
hdu 3247 Resource Archiver(AC自动机+状压DP)
给出m个病毒串,现在要把n个串合并,并且使得合并后的串中不包含病毒串。做得非常心酸,调了半天bug发现有个地方没赋初值,唉,又犯2了……
题解:
http://blog.csdn.net/qian99/article/details/18939153
ZOJ 3494 BCD Code(AC自动机+数位DP)
给出一些禁止串,问从A~B的数字用BCD码表示,并且不包含禁止串的数有多少。AC自动机真是和什么都能结合啊,好神奇~
题解:
http://blog.csdn.net/qian99/article/details/18940379
差不多就到这里了~最近做字符串有些恶心了,该做点儿别的调节调节心情了~以后再做或许还会发?
相关文章推荐
- String.Format格式说明
- 簡單的護眼中藥材
- 将CloudFoundry 部署在私有网络
- HDOJ 1011 Starship Troopers
- POJ Supermarket
- 为什么要学习Python
- 迟 来 的 人 生 总 结
- 使用dispatch_once实现单例模式
- 無需洗牙,教你5分鐘消除牙垢
- How to split a string to array in objective-c?
- 谈谈Serializable、transient、volatile、final在多线程编程中的应用
- SQL Server 局部变量
- [AWS vs Azure] 云计算里AWS和Azure的探究(2)
- Java学习从菜鸟变大鸟之二 输入输出流(IO)
- Java学习从菜鸟变大鸟之二 输入输出流(IO)
- Hdu1233 最小生成树的一些笔记
- remoting与socket、webservice和wcf的比较及优势
- Http1.1与Http1.0的比较
- 框架如何创建App子类的对象(二)?
- 框架如何创建App子类的对象(二)? 推荐