实现 strStr()---字符串匹配的三种解法

题目链接:实现strstr()

题目描述:
实现 strStr() 函数。

给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。

示例 1:

输入: haystack = “hello”, needle = “ll”
输出: 2
示例 2:

输入: haystack = “aaaaa”, needle = “bba”
输出: -1

题目分析:字符串匹配是一道很经典的题目,除了暴力匹配之外,还有比较常见的kmp算法,另外,还有一种利用hash值的RK算法,今天,借着这道题目,我把三种算法的代码都实现一遍

PS:文本串是要查找的串,模式串为要匹配的串
解法1:暴力匹配,每次不匹配,进行下次匹配时,text从这次匹配开始的下一个位置开始,pattern从头开始

例子
比如 text=cdabbcabbda, pattern=abbd
text 从位置2开始与pattern匹配,当匹配到位置5时,发现未能完全匹配,于是下次text从(位置2的下一个)位置3开始匹配,同时,下次pattern从0开始匹配。重复这个过程,直到找到pattern的位置,或者指针到了text末尾,即找不到

代码:

class Solution { 
public int strStr(String haystack, String needle) {
        int n1 = haystack.length(),n2 = needle.length();
        if(n2 == 0) return  0;//特殊值处理
        int p = 0;
        while (p<n1-n2+1){
            //文本串直接从与模式串能够匹配的位置开始匹配
            while(p<n1-n2+1&&haystack.charAt(p)!=needle.charAt(0)) p++;
            //找到以后开始匹配
            int j = 0,len = 0;
            while(p<n1&&j<n2&&haystack.charAt(p)==needle.charAt(j)){
                p++;
                j++;
                len++;
            }
            //完全匹配
            if(len==n2) return p-n2;
            //未完全匹配,将从文本串这次匹配的下一个位置开始匹配
            p = p-len+1;
        }
        return  -1;
    }
}

解法2:kmp算法,暴力匹配时,发现有很多重复的比较过程,km算法就是借助一个辅助数组来尽量的减少这些多余的匹配过程,这个辅助数组存储的数据为:当前字符的前面字符串的最长公共前后缀长度。每次不匹配,进行下次匹配时,text保持不动,pattern不是从头开始,而是从当前字符的前面字符串的最长公共前后缀长度处开始,

例子
比如 text=cdabcabecabcabda, pattern=abcabd
text 从位置0开始与pattern匹配,与第一个位置的不同,继续后移,当匹配完位置abcab时,发现pattern的d与e不匹配,于是下次text保持不动,同时,从辅助数组next中查找当前字符d的值,下次pattern从next[d]开始匹配,字符d的前面的字符串为abcab,最长的公共前后缀长度为2(ab),因此下次pattern从2位置开始匹配,可以看到,由于前面已经比较过的字符串前后缀是相同,因此我们能直接把pattern的前缀和text的后缀部分略过了,直接匹配下一个位置即可。重复这个过程,直到找到pattern的位置,或者指针到了text末尾,即找不到

cd**abcabe**cabcabda//不匹配
    abcabd
 cd**abcabe**cabcabda//移动到next[d]
        abcabd  

代码:

class Solution {
    public int[] get_next(String p){//生成next数组,
        int len = p.length();
        int[] next = new int[len];
        next[0] = -1;
        int k = -1,j = 0;
        while(j < len-1){//防止next[j]越界
            if(k==-1||p.charAt(k)==p.charAt(j)){//如果模式串指针回到了起点或者找到匹配位置 
                k++;
                j++;
                next[j] = k;//存储当前的最大前后缀长度 
            }else{
                k = next[k];//不匹配,将模式串右移 
            }
        }
        return  next;
    }
    public int kmp(String s,String p,int[] next){//过程原理和 get_next一样 
        int s_len = s.length();
        int p_len = p.length();
        int i = 0,j = 0;
        while(i < s_len&&j < p_len){
            if(j==-1||s.charAt(i)==p.charAt(j)){
                i++;
                j++;
            }else{
                j = next[j];//即将模式串右移了(j-next[j])位 
            }
        }
        if(j >= p_len)
            return i - j;
        else
            return -1;
    }
    public int strStr(String haystack, String needle) {
        if(needle.length()==0) return 0;
        int[] next = get_next(needle);
        return  kmp(haystack,needle,next);
    }
}

解法3:Rabin Karp算法,先生成pattern窗口内子串的哈希码,然后再跟pattern字符串的哈希码做比较,相等就匹配。

代码:

class Solution {
    static final long P = Integer.MAX_VALUE;
    static final long BASE = 41;
    //自定义hash函数
    public long getHash(String s) {
        long hashValue = 0;
        for (int i = s.length() - 1; i >= 0; --i) {
            hashValue = (hashValue * BASE + s.charAt(i) - 'a' + 1) % P;
        }
        return hashValue;
    }
    public int strStr(String haystack, String needle) {
        int n1 = haystack.length(),n2 = needle.length();
        if(needle.length()==0) return 0;
        long pHash = getHash(needle);
        int i = 0;
        while (i< n1-n2+1){
            while(i<n1-n2+1&&haystack.charAt(i)!=needle.charAt(0)) i++;
            if(i<n1-n2+1&&getHash(haystack.substring(i,i+n2)) == pHash)
                return i;
            i++;
        }
        return  -1;
    }
}

你可能感兴趣的:(字符串处理)