字符串
字典树
对于常见的字符串匹配问题,在n个字符中查找某个字符串。
若采用暴力方法,需要逐一匹配每个字符串,时间复杂度为O(mn),其中m是字符串的平均长度。
字典树就是模拟查字典操作的数据结构,例如查找dog单词,第一次查找字母d,第二次查找字母o,第三次查找字母g。这样查找每一个单词,查找次数最多只需要这个单词的字母个数。
- 时间复杂度:插入和查找的时间复杂度都是O(m),其中m是待处理字符串的长度。
- 空间复杂度:有公共前缀的单词只需要存一次公共前缀,节省了空间。
字典树应用:
- 字符串检索
- 词频统计:统计一个单词出现了多少次
- 字符串排序:在插入时,在树的平级按字母表的顺序插入。字典树建好之后,用先序遍历就得到了字典树的顺序。
- 前缀匹配:字典树是按照公共前缀来建树的,适合用于搜索提示。
题目描述:
Ignatius最近遇到一个难题,老师交给他很多单词(只有小写字母组成,不会有重复的单词出现),现在老师要他统计出以某个字符串为前缀的单词数量(单词本身也是自己的前缀).
Input
输入数据的第一部分是一张单词表,每行一个单词,单词的长度不超过10,它们代表的是老师交给Ignatius统计的单词,一个空行代表单词表的结束.第二部分是一连串的提问,每行一个提问,每个提问都是一个字符串.
注意:本题只有一组测试数据,处理到文件结束.
Output
对于每个提问,给出以该字符串为前缀的单词的数量.
字典树实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| #include<bits/stdc++.h> using namespace std; struct Trie { Trie* next[26]; int num; Trie() { for(int i=0; i<26; i++) { next[i]=NULL; } num=0; } }; Trie root; void insert(char str[]){ Trie *p=& root; for(int i=0;str[i];i++){ if(p->next[str[i]-'a']==NULL) p->next[str[i]-'a']=new Trie; p=p->next[str[i]-'a']; p->num++; } } int find(char str[]){ Trie *p=& root; for(int i=0;str[i];i++){ if(p->next[str[i]-'a']==NULL) return 0; p=p->next[str[i]-'a']; } return p->num; } int main() { char str[11]; while(gets(str)){ if(!strlen(str)) break; insert(str); } while(gets(str)) cout<<find(str)<<endl; return 0; }
|
KMP
kmp是单模匹配算法,即在一个长度为n的文本串去查找一个长度为m的模式串,它的时间复杂度为O(m+n)。
问题描述:一块花布条,里面有些图案,另有一块直接可用的小饰条,里面也有一些图案。对于给定的花布条和小饰条,计算一下能从花布条中尽可能剪出几块小饰条来呢?
Input
输入中含有一些数据,分别是成对出现的花布条和小饰条,其布条都是用可见ASCII字符表示的,可见的ASCII字符有多少个,布条的花纹也有多少种花样。花纹条和小饰条不会超过1000个字符长。如果遇见#字符,则不再进行工作。
Output
输出能从花纹布中剪出的最多小饰条个数,如果一块都没有,那就老老实实输出0,每个结果之间应换行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| #include<bits/stdc++.h> using namespace std; const int maxn=1000+5; char str[maxn],pattern[maxn]; int next[maxn]; int cnt; int getFail(char *p,int plen){ next[0]=0;next[1]=0; for(int i=1;i<plen;i++){ int j=next[i]; while(j&&p[i]!=p[j]) j=next[j]; next[i+1]=(p[i]==p[j])?j+1:0; } } int kmp(char *s,char *p){ int last=-1; int slen=strlen(s),plen=strlen(p); getFail(p,plen); int j=0; for(int i=0;i<slen;i++){ while(j&&p[i]!=p[j]) j=next[j]; if(s[i]==p[j]) j++; if(j==plen){ if(i-last>=plen){ cnt++; last=i; } } } } int main(){ while(~scanf("%s",str)){ if(str[0]=='#') break; scanf("%s",pattern); cnt=0; kmp(str,pattern); printf("%d\n",cnt); } return 0; }
|