【模板】KMP字符串匹配
題目描述
給出兩個字符串?s_1s1??和?s_2s2?,若?s_1s1??的區間?[l, r][l,r]?子串與?s_2s2??完全相同,則稱?s_2s2??在?s_1s1??中出現了,其出現位置為?ll。
現在請你求出?s_2s2??在?s_1s1??中所有出現的位置。
定義一個字符串?ss?的 border 為?ss?的一個非?ss?本身的子串?tt,滿足?tt?既是?ss?的前綴,又是?ss?的后綴。
對于?s_2s2?,你還需要求出對于其每個前綴?s's′?的最長 border?t't′?的長度。
輸入格式
第一行為一個字符串,即為?s_1s1?。
第二行為一個字符串,即為?s_2s2?。
輸出格式
首先輸出若干行,每行一個整數,按從小到大的順序輸出?s_2s2??在?s_1s1??中出現的位置。
最后一行輸出?|s_2|∣s2?∣?個整數,第?ii?個整數表示?s_2s2??的長度為?ii?的前綴的最長 border 長度。
輸入輸出樣例
輸入 #1復制
ABABABC ABA輸出 #1復制
1 3 0 0 1說明/提示
樣例 1 解釋
。
對于?s_2s2??長度為?33?的前綴?ABA,字符串?A?既是其后綴也是其前綴,且是最長的,因此最長 border 長度為?11。
數據規模與約定
本題采用多測試點捆綁測試,共有 3 個子任務。
- Subtask 1(30 points):|s_1| \leq 15∣s1?∣≤15,|s_2| \leq 5∣s2?∣≤5。
- Subtask 2(40 points):|s_1| \leq 10^4∣s1?∣≤104,|s_2| \leq 10^2∣s2?∣≤102。
- Subtask 3(30 points):無特殊約定。
對于全部的測試點,保證?1 \leq |s_1|,|s_2| \leq 10^61≤∣s1?∣,∣s2?∣≤106,s_1, s_2s1?,s2??中均只含大寫英文字母。
?
題目輸出的第二部分不同于書上的next數組或nextval數組,題目要求輸出的border意思是,到第i個字符時前后綴的相似度(前綴與后綴有一個長度的相同部分則border【i】=1這樣子)。
大話數據結構上的next數組每個位置的值代表的是在當前字符前面的部分前后綴的相似度,而這道題我們要的是包括當前字符以及往前前后綴的相似度,那么我們可以加一個字符a(題目說s1?,s2??中均只含大寫英文字母所以加一個小寫字母不會影響判斷),然后輸出的值全部向后移動一位,即輸出的循環這樣寫:?for(int i=2; i<=l2+1; i++),并且當next【i】不等于0時,要將數據減一,因為k的值是這樣來的:如果前后綴一個字符相等,則k=2,兩個則k=3,n個則k=n+1。
剛開始寫的代碼一直ac70:
//ac70 #include<bits/stdc++.h> using namespace std;char s[1100000],t[1100000]; int l1,l2; int nextval[1100000]; int nextt[1100000]; //求模式串T的next函數修正值并存入數組nextvalvoid get_nextval() {int i=1,k=0;nextval[1]=0;t[l2+1]='a';while(i < l2+1){if(k == 0||t[i] == t[k])//t[i]表示后綴的單個字符,t[k]表示前綴的單個字符{++i;++k;nextt[i]=k;if(t[i]!=t[k])nextval[i]=k;elsenextval[i]=nextval[k];}elsek=nextval[k];} }void Index_KMP(int pos) {int i=pos,j=1;get_nextval();//求到的模式串T的nextval值被存入next數組中while(i<=l1 && j<=l2){if(j == 0 || s[i] == t[j]){++i;++j;}else{j=nextval[j];}}if(j > l2)//如果文本串中都已經匹配到t了{cout<<i-l2<<endl;Index_KMP(i-1);} }int main() {scanf("%s%s",s+1,t+1);//從下標為1的位置開始存l1=strlen(s+1);//從下標為一的位置往后算元素個數l2=strlen(t+1);Index_KMP(1);for(int i=2; i<=l2+1; i++){if(nextt[i]!=0)cout<<nextt[i]-1<<" ";elsecout<<nextt[i]<<" ";}return 0; }錯誤的兩個樣例中我下載了一個查看? 然后我發現問題在于這里的遞歸有問題。
該測試樣例貌似是文本字符串為1000000個A,模板字符串為1000個A
當前面部分全部相同時,我的代碼不能夠接著下一個字符比對
然后我就把遞歸改成了Index_KMP(pos+1),但是我又發現這樣子會導致部分樣例重復查找到同一個相同部分的情況。
然后我發現轉換next數組的方法過于冗雜,可以直接求出borad形式的“next數組”并在與文本字符串匹配時運用,相似度數組輸出時能夠直接正常輸出無需改動后。
因為長度用l1和l2儲存了也無需從下標為1開始存儲,就直接輸入字符串賦值給s和t即可。
那么按照相似的原理就要把 i=1;k=0:變成 i=0;k=-1;原本的next_[1]=0,改為next_[0]=-1,還有if判斷中的k==0改為k==-1(都減了一!因為我們要讓相似度值往前移動一位所以計算時也要把各個值往前移一位)其他部分無需改變。
void get_next() {int i=0,k=-1;next_[0]=-1;while(i<l2){if(k==-1||t[i]==t[k]){++k;++i;nextval[i]=k;if(t[i]!=t[k])next_[i]=k;elsenext_[i]=next_[k];}elsek=next_[k];//若字符不同,則k值回溯} }匹配文本串的過程,每當匹配成功一段之后我們還得繼續比對判斷,這時我們要改變j的值可以避免之前那個代碼遇到的問題
最終輸出的相似度也無需做任何改變直接輸出即可?
?
(其中全局變量設置部分我也進行了改進,因為題目給出的數據范圍為1000000,我們要設置多個數組,范圍都在1000000以上,每次都寫很麻煩,就可以宏定義一個M 1100000,方便程序編寫和改動)
ac代碼:
#include<bits/stdc++.h> using namespace std; #define M 1100000 char s[M],t[M]; int nextval[M]; int next_[M];//nextval的優化數組 int l1,l2; void get_next() {int i=0,k=-1;next_[0]=-1;while(i<l2){if(k==-1||t[i]==t[k]){++k;++i;nextval[i]=k;if(t[i]!=t[k])next_[i]=k;elsenext_[i]=next_[k];}elsek=next_[k];//若字符不同,則k值回溯} } void index_KMP() {int i=0;int j=0;get_next();while(i<l1){if(j==-1||s[i]==t[j]){++i;++j;}elsej=next_[j];if(j==l2){printf("%d\n",i-l2+1);j=next_[j];//j值回溯到合適的位置}} } int main() {cin>>s>>t;l1=strlen(s);l2=strlen(t);index_KMP();for(int i=1; i<=l2; i++)cout<<nextval[i]<<" ";return 0; }總結
以上是生活随笔為你收集整理的【模板】KMP字符串匹配的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: idea git上传代码时上传者的姓名修
- 下一篇: 杭电计算机曾虹,《杭州电子科技大学学报(