算法分类整理+模板②:字符串处理
本周訓(xùn)練賽出了一道kmp模板題,但是由于長時間沒有復(fù)習(xí)字符串處理算法,而且學(xué)習(xí)時也并沒有徹底理解,只是大概明白了思路,所以導(dǎo)致比賽時遲遲沒有做出這一題,最后現(xiàn)場拿出學(xué)校整理的材料現(xiàn)場重新學(xué)習(xí)才ac的這一題。趁這個機會整理一下常用的字符串處理算法以及模板。
?
字符串處理在比賽中一般都不是特別難(至少我遇到的沒有),有的字符串處理會和dp放在一起出題,加大一些難度,而單純的字符串處理其實還是比較好寫。
?
一、strstr
strstr(str1,str2) 函數(shù)用于判斷字符串str2是否是str1的子串。如果是,則該函數(shù)返回str2在str1中首次出現(xiàn)的地址;否則,返回NULL。據(jù)說strstr的效率和kmp差不多。
strstr沒什么好說的,注意返回的是地址,如果要下標就用返回值減去數(shù)組首地址即可。
例題:http://acm.fzu.edu.cn/problem.php?pid=2128
分析:找出所有子串的位置,排序之后找到相鄰兩個子串的第二個字母與倒數(shù)第二個字母的距離,維護最大即可。
代碼:
#include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #define MAXN 1000010 using namespace std; char str[MAXN],tmp[105]; struct node{int start,endn; }no[MAXN]; int cnt=0; bool cmp(node a,node b){return a.start<b.start; } int main(){while(~scanf("%s",str)){int n;scanf("%d",&n);cnt=0;int len=strlen(str);int res=-1;while(n--){scanf("%s",tmp);int pos=0;int ltmp=strlen(tmp);while(strstr(str+pos,tmp)!=NULL){int ans=strstr(str+pos,tmp)-str;no[cnt].start=ans;no[cnt].endn=ans+ltmp-1;pos=no[cnt].endn;cnt++;}}no[cnt].start=no[cnt].endn=len;cnt++;sort(no,no+cnt,cmp);/*for(int i=0;i<cnt;i++)cout<<no[i].start<<" "<<no[i].endn<<endl;*/for(int i=0;i<cnt-1;i++)res=no[i+1].endn-no[i].start-1>res?no[i+1].endn-no[i].start-1:res;if(res==-1)printf("%d\n",len);else printf("%d\n",res);} } strstr應(yīng)用?
二、字符串hash
具體的哈希總結(jié)會另開一專題,這里只貼一下字符串哈希常用的模板:
1.SDBMHash
unsigned int SDBMHash(char *str){unsigned int hash = 0;while (*str){// equivalent to: hash = 65599*hash + (*str++);hash = (*str++) + (hash << 6) + (hash << 16) - hash;}return (hash & 0x7FFFFFFF); }2.BKDRHash
unsigned int BKDRHash(char *str){unsigned int seed = 131; // 31 131 1313 13131 131313 etc..unsigned int hash = 0;while (*str){hash = hash * seed + (*str++);}return (hash & 0x7FFFFFFF); }3.APHash
unsigned int APHash(char *str){unsigned int hash = 0;int i;for (i=0; *str; i++){if ((i & 1) == 0){hash ^= ((hash << 7) ^ (*str++) ^ (hash >> 3));} else{hash ^= (~((hash << 11) ^ (*str++) ^ (hash >> 5)));}}return (hash & 0x7FFFFFFF); }3.kmp
kmp是acm中最常用的字符串處理算法,雖然其效率可能不如Sunday,BM等算法,但是其地位是不容質(zhì)疑的。
kmp算法實質(zhì)是在暴力尋找的基礎(chǔ)上添加了next數(shù)組,其思想就是先對于模式串進行預(yù)處理,然后利用已有的匹配信息,優(yōu)化在查找時候的模式串移動位數(shù)。
舉個例子:
比如主串是:ASDFVAGBASDFGABSDFASDABCBDSFB,模式串是ABCABD。
我們首先從左向右比較,發(fā)現(xiàn)第二位沒有匹配,此時如果是暴力的思想,我們應(yīng)該以主串的第二位為首重新進行匹配,但是其實我們沒有必要這樣做,因為我們可以在主串中找到下一個A開始比較即可。
而我們要做的就是如何利用已知信息去尋找這個next數(shù)組,也就是如何對查詢過程進行優(yōu)化。
由此,我們引出了對于一個字符竄的前后綴概念。前綴是指一個字符串除去最后一個字母并且包含首字母的子串,后綴是指一個字符串除去首字母并且包含尾字母的子串。
舉個例子:
對于字符串:ABCDABD 來說:
- "A"的前綴和后綴都為空集,共有元素的長度為0;
- "AB"的前綴為[A],后綴為[B],共有元素的長度為0;
- "ABC"的前綴為[A, AB],后綴為[BC, C],共有元素的長度0;
- "ABCD"的前綴為[A, AB, ABC],后綴為[BCD, CD, D],共有元素的長度為0;
- "ABCDA"的前綴為[A, AB, ABC, ABCD],后綴為[BCDA, CDA, DA, A],共有元素為"A",長度為1;
- "ABCDAB"的前綴為[A, AB, ABC, ABCD, ABCDA],后綴為[BCDAB, CDAB, DAB, AB, B],共有元素為"AB",長度為2;
- "ABCDABD"的前綴為[A, AB, ABC, ABCD, ABCDA, ABCDAB],后綴為[BCDABD, CDABD, DABD, ABD, BD, D],共有元素的長度為0。
其實,我們也可以這樣理解,next數(shù)組中存儲的就是到這一位為止,有多少和從模式串的頭算起相同的位數(shù)。
比如ABCDABD,next[6]==2,就是到第六位為止,對于這個子串存在一個兩位的既存在于前綴中,也存在于后綴中,也就是在串中和模式串首重復(fù)的字串:AB。
而當我們在進行kmp時,模式串向后移動的距離就不簡簡單單是1位,而是“移動位數(shù) = 已匹配的字符數(shù) - 對應(yīng)的部分匹配值”。
比如我們對于模式串ABCDABD來說,此時主串為ABCDABACDABCDABD,模式串的next數(shù)組前面已經(jīng)得到,為0,0,0,0,1,2,0。首先我們匹配到第七位失配,就需要向后移動(已匹配的6位-最后一個匹配位所對應(yīng)的next值2)=4位,這樣就極大的提高了效率。
?
const int MAXN_T=1000010; const int MAXN_P=10010; char P[MAXN_P],T[MAXN_T]; int _next[MAXN_P]; void init_next(char *P){int m=strlen(P+1); //數(shù)組下標從1開始_next[1]=0; //next數(shù)組第一位是0for(int k=0,q=2;q<=m;q++){ //q是模板字符串下標,k是最大前后綴長度while(k>0 && P[k+1]!=P[q]) //遞歸求P串的各位最大相同前后綴長度k=_next[k];if(P[k+1]==P[q]) //如果兩位相等,最大相同前后綴長度加1k++;_next[q]=k;} } 求next數(shù)組模板 int kmp(char *P,char *T){int n=strlen(T+1),m=strlen(P+1);init_next(P);int sum=0;for(int i=1,q=0;i<=n;i++){ //i是T串下標,q是P串下標while(q>0&&P[q+1]!=T[i]) //根據(jù)最大前后綴長度找應(yīng)該向后移動多少位q=_next[q];if(P[q+1]==T[i])q++;if(q==m){ //如果P串匹配到最后一位并且成功sum++; //則計數(shù)加1q=_next[q]; //可以繼續(xù)查詢T串中共出現(xiàn)P串多少次} //如果只查詢是否存在可以直接在這return }return sum; } kmp模板?
其余關(guān)于字符串處理的算法比如自動機,后綴數(shù)組,BM,Sunday,暫時還沒有太深的了解,等到有了時間和機會,會再來補上!
轉(zhuǎn)載于:https://www.cnblogs.com/Torrance/p/5445946.html
總結(jié)
以上是生活随笔為你收集整理的算法分类整理+模板②:字符串处理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Mybatis接口注解开发
- 下一篇: bzoj 2330: [SCOI2011