题目链接:实现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;
}
}