字符串匹配算法

来源:互联网 发布:cookie的js代码调用 编辑:程序博客网 时间:2024/06/05 10:23

字符串匹配算法

  • 字符串匹配算法
        • 朴素字符串匹配算法
        • Rabin-Karp算法
        • 有限自动机算法
        • KMP算法
        • 笔面高频题
          • 单词间的逆序调整
          • 前n字符后移
          • 两字字符按照字典序最小拼接
          • 判断两个字符串是否互为旋转词

朴素字符串匹配算法

//朴素字符串匹配算法(输出偏移量)/* * 朴素字符串匹配算法是通过一个循环找到所有有效偏移,该循环对n-m+1可能的s值进行检测, * n是要在上面进行查找的对象大小,m是所要查找的目标的大小,s是偏移量。 * 朴素字符串匹配算法的匹配时间为O((n-m+1)*m) * */void nativeStringMatchar(string str, string com){    for(int i = 0; i <= int(str.length()-com.length()); i++){        bool judge = true;        for(int j = 0; j < int(com.length()); j++){            if(str[j+i] != com[j]){                judge = false;                break;            }        }        if(judge) cout << i << ' ';    }}

Rabin-Karp算法

//Rabin-Karp算法/* * 字符串 * n = 12345678 * m = 1234 * lm = m.length() * * 步骤: * 1.预处理: * 预处理就是计算出字符串m的哈希值,我们计算哈希值的算法就是把这些字符关联在一个多项式里面, * 多项式的模式就是 m[0]*x^lm-1 + m[1]*x^lm-2 + .... + m[lm-1]*x^0 这种形式的,这样就把一串字符关联在了一个多项式里面, * 然后我们利用霍纳法则优化多项式的计算,这样的话,我们可以在O(m)时间内,计算出字符串的哈希值 * * 2.搜索 * 搜索的话就是从字符串n里面第一个lm长的字符串开始,不断地比较这个字符串与字符串m的哈希值,如果哈希值相等的话, * 就再去一个字符一个字符的比较一下是否相等,这样,我们最快,可以在O(n-m+1)的时间内比较完,最坏的情况是O((n-m+1)*m) * 但是平均时间来看,rk算法要比朴素算法快得多 * * 关键点: * 1.哈希算法的关键,就是计算关联这一串字符串的多项式,每个字符值都是多项式的一个系数 * 2.利用霍纳法则优化多项式的计算 * 3.如果明白了哈希值的计算是多项式相加的结果,就不难明白计算n字符串里面其余lm长度字符串大小的哈希值的算法了 * * */ void rabinKarp(string str, string com){     int hStr = 0, hCom = 0;     int lCom = com.length();     int lStr = str.length();     int d = (1<<(lCom-1));//多项式第一项的系数,x^m-1     //预处理(使用霍纳法则进行优化多项式的运算)     for(int i = 0; i < lCom; i++){         hStr = (hStr<<1) + str[i];         hCom = (hCom<<1) + com[i];     }     //搜索     for(int i = 0; i <= lStr - lCom; i++){         if(hStr == hCom){             bool judge = true;             for(int j = 0; j < lCom; j++){                 if(str[i+j] != com[j]){                     judge = false;                     break;                 }             }             if(judge) cout << i << ' ';         }         //计算下一个模式的哈希值,思路就是去掉第一个字符的多项式的值,加上新加的字符的多项式的值         if(i != lStr-lCom)            hStr = ((hStr-str[i]*d)<<1) + str[i+lCom];     } } /*  * 当Rabin-karp算法中字符串的哈希值过大时,出现问题就是我们不能在用常数的时间去计算一个字符串的哈希值,  * 解决方法就是利用每个哈希值都对一个数取模解决,这样数就能变小了,  * 但是还有一个问题是,并不是所有模相等的数都相等,但是模不相等的数肯定不相等,这样就会出现很多伪命中点,  * 所以,我们只能用这个方法来判断是否不相等,相等时的误差率会增大  *  * */ void rabinKarpForBig(string str, string com){     int hStr = 0, hCom = 0;     int lCom = com.length();     int lStr = str.length();     int q = 13;     int d = (1<<(lCom-1))%q;//多项式第一项的系数,x^m-1     //预处理(使用霍纳法则进行优化多项式的运算)     for(int i = 0; i < lCom; i++){         hStr = ((hStr<<1) + str[i])%q;         hCom = ((hCom<<1) + com[i])%q;     }     //搜索     for(int i = 0; i <= lStr - lCom; i++){         if(hStr == hCom){             bool judge = true;             for(int j = 0; j < lCom; j++){                 if(str[i+j] != com[j]){                     judge = false;                     break;                 }             }             if(judge) cout << i << ' ';         }         //计算下一个模式的哈希值,思路就是去掉第一个字符的多项式的值,加上新加的字符的多项式的值         if(i != lStr-lCom){            hStr = (((hStr-str[i]*d)<<1) + str[i+lCom])%q;            if(hStr < 0) hStr += q;         }     }}

有限自动机算法

//构建转移函数二维表void makeJumpTable(string p){    int m = p.length();    int alphaSize = 4;    for(int q = 0; q <= m; q++)        for(int k = 0; k < alphaSize; k++){            char c = (char)('1' + k);            string Pq = p.substr(0, q) + c;            int nextState = findSuffix(Pq, p);            cout << "from state " << q << " receive input char " << c << " jump to state " << nextState << endl;            map<char, int> m = jumpTable[q];            m[c] = nextState;            jumpTable[q] = m;        }}int match(string T, string p){    int q = -1;    for(int n = 0; n <= int(T.length()); n++){        map<char, int> m = jumpTable[q];        int oldState = q;        q = m[T[n]];        if(q == -1) return -1;        cout << "In state " << oldState << " receive input " << T[n] << " jump to state " << q << endl;        if(q == int(p.length())) cout << n << ' ';    }    return -1;}

KMP算法

//KMP算法int Pi[500];//找最长后缀大小int getLongestSuffix(int s, string P) {    if(s <= 0 || s > int(sizeof(Pi)/4)) return -1;    if(Pi[s] != -2) return Pi[s];    Pi[s] = 0;    int k = getLongestSuffix(s-1, P);    do{        if(P[k] == P[s-1]) return Pi[s] = k + 1;        if(k > 0) k = getLongestSuffix(k, P);    }while(k > 0);    return Pi[s];}int KMP(string T, string P) {    int m = P.length();    for(int i = 0, q = 0; i < int(T.length()); i++) {        //如果遇到一个不相等的,就往后移,移动的长度就是我们找到的最长后缀大小,直到找到一个相等的        while(q > 0 && P[q] != T[i]) q = Pi[q];        //如果遇到相等的,就继续        if(P[q] == T[i]) q = q + 1;        //当相等的个数为待匹配字符时,ok,说明包含        if(q == m) return i-m+1;    }    return -1;}

笔面高频题

单词间的逆序调整
//单词间的逆序调整//思路:逆序遍历一遍字符串,遇到空格就输出单词,把第一个和最后一个特殊情况处理一下就ok了string wordInvert(string str){    string result;    int end;    end = str.length();    for(int i = str.length(); i >= 0; i--){        if(str[i] == ' ' || i == 0) {            if(i != 0)                result += str.substr(i+1, end-i-1);            else                result += str.substr(i, end-i+1);            if(i != 0)                result += " ";            end = i;        }    }    return result;}void invert(string& str, int start, int end){    int len = end - start + 1;    for(int i = start; i < start + len/2; i++){        char tmp = str[i];        str[i] = str[start+len-(i-start)-1];        str[start+len-(i-start)-1] = tmp;    }}
前n字符后移
//将前index个字符移到后面//思路,整体逆序,然后再部分逆序string wordInvert(string str, int index){    invert(str, 0, str.length()-1);    invert(str, 0, str.length()-index-2);    invert(str, str.length()-index-1, str.length()-1);    return str;}
两字字符按照字典序最小拼接
//将两个给定的字符串按照最小字典序进行拼接string wordJoin(string one, string two){    string tOne = one+two, tTwo = two+one;    int lOne = int(tOne.length()), lTwo = int(tTwo.length());    int len = lOne > lTwo ? lOne : lTwo;    for(int i = 0; i < len; i++){        //如果短的字符串是长的字符串的前缀的话,就返回短的字符串        if(lOne < i+1 && lTwo >= i+1)            return tOne;        else if(lTwo < i+1 && lOne >= i+1)            return tTwo;        else if(lTwo == lOne && lTwo == i+1)            return tTwo;        //如果一个字符串的某个字符小于另一个,则小        else if(tOne[i] < tTwo[i])            return tOne;        else if(tOne[i] > tTwo[i])            return tTwo;    }}
判断两个字符串是否互为旋转词
//判断两个字符串是否互为旋转词/* * 1.先判断字符串长度是否相等,不相等直接false * 2.字符串长度相等的话,就把其中一个字符串使其自身相加 * 3.把两一个字符串与自身相加的字符串进行kmp算法比较,看看是否包含在其中 * * 关键点:一个字符串自身相加的话,就包含了他自己所有的旋转词 * * */bool judgeRotateWord(string one, string two){    if(one.length() != two.length())        return false;    string tmp = one+one;    if(KMP(tmp, two))        return true;    return false;}
0 0
原创粉丝点击