#include<iostream>#include<string>#include<cstring>#include<cstdio>using namespace std;const int N=100000;//文本串的最大長度const int M=100;//模式串的最大長度int n;//文本串的實際長度int m;//模式串的實際長度char T[N];//文本串char P[M];//模式串int pre[N];//kmp裡面的首碼函數/***************************************************************************字串匹配--樸素匹配演算法:若串T中從第s(S 的下標0≤s<n-m+1)個字元起,存在和串P相同的子串,則稱匹配成功,返回第一個這樣的子串在串T中的下標,否則返回 -1。*****************************************************************************/int Index_BF(){ int s=0 , j= 0; while(s<= n-m) { if ( T[s+j] == P[j] ) j++; // 一個字元匹配,繼續比較後一字元 else { s++; j = 0; // S移動一個位置,重新開始新的一輪匹配過程,模式P的指標回到首部 } if ( j == m ) return s; // 匹配成功,返回下標 } return -1; // 串T中不存在和串P相同的子串}/***************************************************************************字串匹配--Sunday演算法:在匹配過程中,模式串並不被要求一定要按從左向右進行比較還是從右向左進行比較;它在發現不匹配時,演算法能跳過儘可能多的字元以進行下一步的匹配,從而提高了匹配效率。Sunday演算法思想跟BM演算法很相似,在匹配失敗時關注的是文本串中參加匹配的最末位字元的下一位字元。• 如果該字元沒有在匹配串中出現,則直接跳過,即移動步長 = 模式串的長度+1;• 否則,移動步長 = 模式串中最右端的該字元到模式串末尾的距離+1。*****************************************************************************/int sunday(){ int n = strlen(T); //文本串的長度 int m = strlen(P); //模式串的長度 int next[256] = {0}; //記錄模式串中每個字元到最右邊的最短距離+1的值 for (int j = 0; j < 256; ++j) { //將每個字元到最右邊的最短距離+1的值全部初始化為m+1,即最大值 next[j] = m + 1; } for (int j = 0; j < m; ++j) { //記錄模式串中每個字元到最右邊的最短距離+1 //例如:p = "abcedfb" //next = {7 1 5 4 3 2 8 8 8 8 8 ........} next[ (int)P[j] ] = m - j; } int pos = 0; while (pos < (n - m + 1)) //末端對齊 { int i = pos; int j; for (j = 0; j < m; ++j, ++i) { if (T[i] != P[j]) { //不等於就跳躍,跳躍是核心 pos += next[(int)T[pos + m] ]; break; } } if ( j == m ) return pos; } return -1;}/***************************************************************************字串匹配--ZZL演算法:首先在文本串T中尋找模式串P的首字母;每找到一個則將它的位置儲存,然後依次提取這些位置;從這些位置開始繼續匹配模式串P。對於頻繁使用的要匹配的主串和模式串來說;由於預先儲存了模式串在主串中的所有儲存位置,所以匹配速度會非常快。*****************************************************************************/int k; //記錄為模式串P首字母在主串中出現的次數int v; //記錄模式串P首字母在主串中出現的次數int x[N]; //模式串P首字元在文本串中的所有出現位置,並將其儲存在一個數組x中int s[N]; //模式串在文本串中匹配時,記錄想x[i]的下標int zzl(){ int n = strlen(T); //文本串的長度 int m = strlen(P); //模式串的長度 //尋找模式串P首字元演算法 k = 0; for(int i=0; i<n-m+1; i++) { if(T[i] == P[0]) { x[k] = i; k++; } } //匹配演算法 v=0; int j; for(int i=0; i<k; i++) { for(j=1; j<m; j++) { if(T[ (x[i]+j) ] != P[j]) { break; } } if(j == m) { s[v] = i; v++; } } return v;}/***************************************************************************字串匹配--RK演算法:通過對字串進行雜湊運算(散列運算);即給文本中模式長度為m的字串雜湊出一個數值;然後只需比較這個數值即可;之後在數值的基礎上再用樸素演算法比較字串。*****************************************************************************/bool NativeStringMatcher(const char *T, int s, const char *P)//樸素匹配演算法,Rabin_Karp調用{ int m = strlen(P); int j; for (j = 0; j < m; j++) { if (T[s+j] != P[j]) { return false; } } if (j == m) { return true; } return false;}void Rabin_Karp(int d, int q)//RabinKarp演算法{ int n = strlen(T); int m = strlen(P); int h = 1; for(int i = 0; i < m - 1; i++) //計算h=d^(m-1) mod q { h *= d; //h=h*d,pow可能會越界,所以用乘法 if (h >= q) { h %= q; //h=h % q } } int p = 0; int t = 0; for (int i = 0; i < m; i++) //預先處理,計算p和t { p = (d * p + (P[i] - '0')) % q; //P[i] - '0'就是將字元轉換為數字 t = (d * t + (T[i] - '0')) % q; } for (int i = 0; i < n - m+1; i++) { printf("t%d = %d\n", i, t); if (p == t) { if (NativeStringMatcher(T,i, P)) { printf("匹配位置是:%d\n", i); } else { printf("偽命中點:%d\n", i); } } if (i < n - m) { t = (d * (t - h * (T[i] - '0')) + T[i + m] - '0') % q; if (t < 0) { t += q; } } }}/***************************************************************************字串匹配--KMP演算法:在發生失配時,文本串不需要回溯;而是利用已經得到的“部分匹配”結果將模式串右移儘可能遠的距離,繼續進行比較。這裡要強調的是:模式串不一定向右移動一個字元的位置;右移也不一定必須從模式串起點處重新試匹配;即模式串一次可以右移多個字元的位置,右移後可以從模式串起點後的某處開始試匹配。*****************************************************************************///計算模式P的首碼函數void compute_preflx( ){ int k=0; //計算模式子串的最長首碼 pre[1] = 0; //首碼函數,從下標1開始 for( int q = 2; q <= m; ++q ) //對模式串從第2個字元開始計算其首碼函數值 { while( k > 0 and P[k+1] != P[q] ) //沒有最大的首碼了 k = pre[k]; if( P[k+1] == P[q]) k ++; pre[q] = k; } for( int i=1; i<=m; ++i ) cout<<pre[i]<<" "; cout<<endl;}void kmp( ){ int q = 0; //匹配的字元個數,也是作為模式p的下標使用的 compute_preflx(); //計算出模式p的首碼函數 for( int i = 1; i<=n; ++i ) //對文本字元從左向右掃描,指標i是不回縮的 { while( q >0 and P[q+1] != T[i] ) //當模式p中的下一個字元不與文本字元匹配時, q = pre[q]; //模式p的下標去要回縮 if( P[q+1] == T[i] ) //當模式p中的下一個字元與文本字元匹配時 q++; //將模式p的下標+1 if( q == m ) // 即模式p的所有字元都與文本字元匹配 { cout<<"s="<<i-m<<endl; //列印出有效位移s q = pre[q]; //尋找下一個匹配 } }}/***************************************************************************字串匹配--Horspool演算法:對於每個文本搜尋視窗,將視窗內的最後一個字元(例如β)與模式串的最後一個字元進行比較。如果相等,則繼續從後向前驗證其他字元,直到完全相等或者某個字元不匹配。然後,無論匹配與否,都將根據β在模式串的下一個出現位置將視窗向右移動。*****************************************************************************/void HorspoolMatch(){ int n = strlen(T); //文本串的長度 int m = strlen(P); //模式串的長度 if (m > n) { return ; } short skip[256]; //記錄模式串中每個字元到最右邊的最短距離的值 for(int i = 0; i < 256; i++) { skip[i] = m; } //計算模式串P中的每個字元到最右邊的最短距離 for(int i = 0; i < m - 1; i++) { skip[P[i]] = m - i - 1; } int pos = 0; while(pos <= n - m) { int j = m -1; //從後面向前面匹配 while(j >= 0 && T[pos + j] == P[j]) { j--; } if(j < 0) { cout<<"an occurrence at:"<<pos<<endl; } pos = pos + skip[T[pos + m -1]]; //跳到m-1字元在模式串最右邊的位置 }}int main(){ while(gets(T)) { gets(P); kmp(); HorspoolMatch(); Rabin_Karp(10, 13); zzl(); sunday(); } return 0;}