Files
2022-WangDao-CS-DS-Notes/4.3字符串模式匹配.md
2022-03-28 16:49:12 +08:00

3.2 KiB
Raw Blame History

字符串模式匹配

字符串模式匹配:在主串中找到与模式串相同的子串,并返回其所在位置。

子串:一定能在主串中找到的串

模式串:不一定能在主串中找到的串

方法:朴素模式匹配算法KMP算法

一、朴素模式匹配算法

1.用串的定位操作:

方法在S中依次按顺序取m长子串判断是否与T相同

//定位操作
int Index(SString S,SString T){
    int i = 1, n = StrLength(S), m = StrLength(T);
    SString sub;
    while(i < n-m+1){
        SubString(sub, S, i, m);
        if(StrCompare(sub, T) != 0) ++i;
        else return i; //返回子串在主串中的位置
    }
    return 0;          //S中不存在与T相同的子串
}

2.双指针算法

int Index(SString S, SString T){
    int i = 1, j = 1;
    while(i<=S.length && j<=T.length){  //跳出循环情况j>T.length,匹配成功
        if(S.ch[i]==T.ch[i]){			             //i>S.length,匹配失败
            ++i; ++j;          //继续比较后面的字符
        }else{				   //匹配失败指针i、j都后退匹配下一个
            i = i-j+2;
            j = 1;             
        }
    }
    if(j > T.length){          //j>T.length,匹配成功
        return i-T.length;
    }else{
        return 0;
    }
}

朴素模式匹配算法的时间复杂度:

设主串长度为n模式串长度为m

最好情况每次匹配第一个字符就匹配失败直到最后才匹配成功循环n次最好时间复杂度=O(n)

最坏情况到最后也没找到主串移动n个元素模式串移动m个元素最坏时间复杂度=O(mn)

二、KMP算法

精髓利用好已经匹配过的模式串信息建立一个next数组表示j的回溯

int Index(SString S, SString T){
    int i = 1, j = 1;
    while(i<=S.length && j<=T.length){  //跳出循环情况j>T.length,匹配成功
        if(S.ch[i]==T.ch[i]){			             //i>S.length,匹配失败
            ++i; ++j;          //继续比较后面的字符
        }else{				   //匹配失败指针i不变j后退匹配下一个
            j=next[j];            
        }
    }
    if(j > T.length){          //j>T.length,匹配成功
        return i-T.length;
    }else{
        return 0;
    }
}

KMP算法的时间复杂度

设主串长度为n模式串长度为m

最好情况每次匹配第一个字符就匹配失败直到最后才匹配成功循环n次最好时间复杂度=O(n)

最坏情况到最后也没找到主串移动n个元素模式串移动m个元素最坏时间复杂度=O(m+n)

next数组的建立

P[0 ~ k-1] == P[j-k ~ j-1]

img

//next数组的建立
int getNext(String ps){
    char[] p = ps.toCharArray();
    int[] next = new int[p.length];
    next[1] = 0;
    int j = 1, k = 0;
    while (j < p.length){
    	if (k == 0 || p[j] == p[k]) {
            next[++j] = ++k;
        }else{
            k = next[k];
    	}
    }
}