字符串匹配算法

字符串匹配算法

朴素字符串匹配算法
//朴素字符串匹配算法(输出偏移量)
/*
 * 朴素字符串匹配算法是通过一个循环找到所有有效偏移,该循环对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
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值