KMP Trie 例题讲解
文章目錄
- HDU 4763 Theme Section
- 題意:
- 題解:
- 代碼:
- POJ 3630 Phone List
- 題意:
- 題解:
- 代碼:
- HDU 3746 Cyclic Nacklace
- 題意:
- 題解:
- 代碼:
- HDU 2087 剪花布條
- 題意:
- 題解:
- 代碼:
- HDU 1251 統計難題
- 題意:
- 題解:
- 代碼:
- HDU 2072 單詞數
- 題意:
- 題解:
- 代碼:
- POJ 2513 Colored Sticks
- 題意:
- 題解:
- 代碼:
HDU 4763 Theme Section
題意:
題解:
代碼:
POJ 3630 Phone List
題意:
n個號碼,如果有一個號碼是其他號碼的前綴,則輸出NO,否則輸出YES
題解:
根據字典序排序,然后判斷相鄰的兩個字典序是否有前綴
代碼:
#include<iostream> #include<cstdio> #include<string> #include<algorithm> using namespace std; char s[30]; string w[100005]; int main() {int T,n;bool F=0;scanf("%d",&T);while(T--){scanf("%d",&n);F=0;//memset(w,0,sizeof(w));for(int i=0;i<n;i++)cin>>w[i];sort(w,w+n);for(int i=1;i<n;i++){if(w[i].length()>w[i-1].length()){int j;for(j=0;j<w[i-1].size();j++)if(w[i][j]!=w[i-1][j])break;if(j==w[i-1].size())F=1; }}if(F) cout<<"NO"<<endl;else cout<<"YES"<<endl;}}靜態數組寫字典樹做法:
#include <cstdlib> #include <cstdio> #include <cstring> typedef struct node //節點的結構體 {bool end;//結束的標志int a[10]; }node; node phonelist[100000]; int x; int flag; void Init() {flag=1;//標志x=0; //初始位置;for(int i=0;i<100000;i++){phonelist[i].end=false;for(int j=0;j<10;j++)phonelist[i].a[j]=-1;} } int build(char *s) //建立字典樹 {int k,now=0,tag=0;// 初始位置int len=strlen(s);for(int i=0;i<len;i++){k=s[i]-'0';if(phonelist[now].a[k]==-1){tag=1;//說明進入一個新的位置phonelist[now].a[k]=++x; //給數組賦值now=x;//下一個位置}else{now=phonelist[now].a[k];if(phonelist[now].end)return 0; //單詞的結束標志}}phonelist[now].end=true; //標記結束的節點if(!tag) return 0;return 1; } int main() {//freopen("1.txt","r",stdin);int n,m;char s[12];scanf("%d",&n);while(n--){Init();scanf("%d",&m);for(int i=0;i<m;i++){scanf("%s",s);if(flag)flag=build(s);}if(flag)printf("YES\n");elseprintf("NO\n");}return 0; }HDU 3746 Cyclic Nacklace
題意:
一個字符串,在串后面補全幾個字符,可以使他的全部字符最少循環2次以上,求最少補幾個字符?
題解:
kmp算法的next數組
next數組的含義為, T[0…i-1]中的最大公共真前綴/后綴, 對于0-indexed求出的next數組實際上為1-M
next[M]為T中的最大公共真前綴/后綴, 這樣M-next[M]應該就是循環節的長度len(仔細思考)
如果next[M]為0說明沒有循環節, M%len==0說明循環節完全重復包含
首先這里用到一個kmp算法的最長相等前后綴算法next[]
然后很重要的一點:補全后字符串循環體L為strlen(s)-next[n]
這里有三種可能性:
1,字符串里已經有兩次以上循環,而且都已補全這種就不用補全了,判斷方式: 相等前后綴肯定不為零,且總長n%L==0
2,字符串里已經有兩次以上循環,后續循環未補全這種要把單次循環的補全,由于已經有兩次以上的循環,那么前后綴肯定要比單次循環要長(懶得證明)那么 前后綴%單次循環 就是循環剩下來的: 比如 ababa 前后綴是aba 單次循環是 ab 剩下來的就是a那么我們要補全的就是 單詞循環-剩下來的: b ababa + b = ababab
3, 字符串里沒有兩次以上循環這種要補全兩次由于沒有兩次以上的循環,那么前后綴肯定要比單次循環要短(同上)那么我們就輸出 單詞循環-前后綴: l-next[n]這種可以和第二種合并
代碼:
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int MAX=1e5+13; char w[MAX]; int cnt[MAX]; int F; void JZY(int len ) {F=-1;cnt[0]=-1;for(int i=0;i<len;){if(F==-1||w[i]==w[F]) cnt[++i]=++F;else F=cnt[F];} } int main() {int T;int m;scanf("%d",&T);while(T--){scanf("%s",w);int len=strlen(w);JZY(len);m=len-cnt[len];if((m!=len)&&(len%m==0)) printf("0\n");else printf("%d\n",m-cnt[len]%m);} }HDU 2087 剪花布條
題意:
一個目標串,一個模板串,問目標串最多可以剪除幾個模板串?
題解:
可以用kmp來做,也可以用hash來做
代碼:
hash代碼:
#include<iostream> #include<vector> #include<algorithm> #include<string.h> #include<string> #include<cstdio> using namespace std; #define maxn 1001 string a; string b; const int B = 100000007; typedef unsigned long long ull; ull _hash(int al, int bl) {if (al < bl)return 0;ull t = 1, ah = 0, bh = 0, cnt = 0;for (int i = 0; i < bl; i++)t *= B;for (int i = 0; i < bl; i++)ah = ah*B + a[i];for (int i = 0; i < bl; i++)bh = bh*B + b[i];for (int i = 0; i + bl<=al; i++){//cout << a << "\n" << b << endl;//cout << ah << "\t" << bh << endl;if (ah == bh){cnt++;ah = ah - a[i + bl - 1] + '$';//這是關鍵一步,更改字母之后hash值發生變化,所以變化量為'$'-a[i+bl-1]a[i + bl-1] = '$';//更改已匹配的最后一個字母}if (i+bl<al)ah = ah*B - a[i] * t + a[i + bl];}return cnt; } int main() {while (cin>>a&&a!="#"){ cin >> b;int al = a.length();int bl = b.length();ull res = _hash(al, bl);cout << res << endl;} }kmp代碼:
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> using namespace std; const int maxx=1e6+10; int Next[maxx]; char s[maxx],p[maxx]; int len1,len2; void getnext() {int i=0,j=-1;Next[0]=-1;while(i<len1){if(j==-1||p[i]==p[j]){++i;++j;if(p[i]==p[j])Next[i]=Next[j];elseNext[i]=j;}elsej=Next[j];} } void getkmp() {int num=0,i=0,j=0;while(i<len2){if(j==-1|s[i]==p[j]){++i;++j;}elsej=Next[j];if(j==len1){++num;j=0;}}printf("%d\n",num); } int main() {while(scanf("%s",s),s[0]!='#'){scanf("%s",p);len1=strlen(p);len2=strlen(s);getnext();getkmp();}return 0; }HDU 1251 統計難題
題意:
統計出以某個字符串為前綴的單詞數量
題解:
模板題,map也可以做
代碼:
#include<stdio.h> #include<string.h> #include<stdlib.h> #include<math.h> #include<algorithm> #include<iostream> #include<vector> #include<stack> #include<queue> #include<map> #include<set> #define inf 0x3f3f3f3f using namespace std; typedef long long LL; const int N=1e6+1; const int mod=1e9+7; const double pi=acos(-1); const double eps=1e-8; char s[12]; int trie[N][26],sum[N],tot,root; void sert() {root=0;for(int i=0;s[i]!='\0';i++){int id=s[i]-'a';if(!trie[root][id]) trie[root][id]=++tot;sum[trie[root][id]]++;root=trie[root][id];} } int finf() {root=0;int id;for(int i=0;s[i]!='\0';i++){id=s[i]-'a';if(!trie[root][id]) return 0;root=trie[root][id];}return sum[root]; } int main() {while(gets(s)){if(s[0]=='\0') break;sert();}while(gets(s)){printf("%d\n",finf());} }HDU 2072 單詞數
題意:
統計不同單詞的數量
題解:
可以用map來做
不過練習字典樹
代碼:
#include<iostream> #include<sstream> using namespace std;struct node{int flag;struct node *next[26];node(){for(int i=0;i<26;i++){this->next[i]=NULL;}this->flag=0;} };node *root; int ans; void Insert(string str) {node *p=root;for(int i=0;i<str.size();i++){if(p->next[str[i]-'a']==NULL)p->next[str[i]-'a']=new node();p=p->next[str[i]-'a'];}p->flag++;if(p->flag==1)ans++; }int main() {string str,str1;while(getline(cin,str)){root=new node();ans=0;if(str=="#")break;istringstream ss(str);while(ss>>str1){Insert(str1);}cout<<ans<<endl;}return 0; } #include<iostream> #include<cstdio> #include<string> #include<algorithm> #include<set> #include<set> #include <sstream> using namespace std; int main() { string a; while(getline(cin,a)&& a != "#"){ istringstream stream(a);string w; set<string> map; while(stream >>w) map.insert(w); printf("%d\n",map.size()); }}POJ 2513 Colored Sticks
題意:
給你一堆木棍。每根棍子的每個端點都用某種顏色著色。有沒有可能把棍子在一條直線上對齊,使接觸的端點的顏色是相同的顏色?
題解:
歐拉通路+并查集+字典樹
判斷歐拉通路是否存在的方法
有向圖:圖連通,有一個頂點出度大入度1,有一個頂點入度大出度1,其余都是出度=入度。
無向圖:圖連通,只有兩個頂點是奇數度,其余都是偶數度的。
代碼:
#include <cstdio> #include <cstring> int const MAX = 500005; int fa[MAX], d[MAX], cnt;struct Trie {int sz, t[MAX][15];int jud[MAX];Trie(){sz = 1;memset(t[0], -1, sizeof(t));jud[0] = 0;}void clear(){sz = 1;memset(t[0], -1, sizeof(t));jud[0] = 0;}int idx(char c){return c - 'a';}void insert(char* s, int v){int u = 0, len = strlen(s);for(int i = 0; i < len; i++){int c = idx(s[i]);if(t[u][c] == -1){memset(t[sz], -1, sizeof(t[sz]));jud[sz] = 0;t[u][c] = sz++;}u = t[u][c];}jud[u] = v;}int search(char* s){int u = 0, len = strlen(s);for(int i = 0; i < len; i++){int c = idx(s[i]);if(t[u][c] == -1) return -1;u = t[u][c];}if(jud[u]) return jud[u];return -1;} }t;void Init() {for(int i = 0; i < MAX; i++)fa[i] = i; }int Find(int x) {return x == fa[x] ? x : fa[x] = Find(fa[x]); }void Union(int a, int b) {int r1 = Find(a);int r2 = Find(b);if(r1 != r2)fa[r1] = r2; }bool eluer() {int sum = 0, t = -1;for(int i = 1; i < cnt; i++)if(d[i] % 2) sum++;if(sum != 0 && sum != 2)return false;for(int i = 1; i < cnt; i++){if(t == -1)t = Find(i);else if(Find(i) != Find(t)) return false;}return true; }int main() {char s1[20],s2[20];cnt = 1;Init();t.clear();while(scanf("%s %s", s1, s2) != EOF){if(t.search(s1) == -1)t.insert(s1, cnt++);int u = t.search(s1);if(t.search(s2) == -1)t.insert(s2, cnt++);int v = t.search(s2);Union(u, v);d[u]++;d[v]++;}if(eluer())printf("Possible\n");elseprintf("Impossible\n"); }總結
以上是生活随笔為你收集整理的KMP Trie 例题讲解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Manacher 例题讲解
- 下一篇: 东风汽车全新品牌“奕派”发布,首款车型