leetcode28__java

target目标字符串  简称为t    长度为lent

pattern模板字符串   简称为p   长度为lenp

第一种方法:

用pattern的每一位开始的长度为lent的字符串去和target比 这种做法肯定不会错处

第二种方法(KMP):

第一个鲁莽的想法:假设target是abc   然后去比 假设目标是abdabc  那么显然会想 :第4位出错了 好 移位 有必要移一位吗?直接移2位不就好了?看上去并不会出错啊,对的 ,这个例子并不会出错,既然是c没对上,那就说明target的前面两位是ab,那干嘛明知道是ab还要移一位去拿a跟b比呢?是不是很蠢?

       然而当然会出错,换一个pattern:ABCABD,然后target是ABCABCABD,按照我们之前简单的鲁莽的想法,第6位出错了,C对不上D,好,移,移几位?直接移到第7位去比?那就错过了第4位开始的ABCABD,所以不能直接移到下一位,究其原因,是因为,你看,pattern:ABCABD,是有特点的,有AB这个重复的对不对,就是说第6位D出错的时候,我们要想:当前target的前面5位肯定是ABCAB,只不过当前这位不是D,那么我们要找ABCABD,应该退两位给那个AB一个机会,看看它对不对的上,

也就是:当发现第6位D出错时

ABCABCABD
ABCABD   

万无一失 效率低:正确姿势
ABCABCABD
 ABCABD  

鲁莽瞎跳:错误
ABCABCABD
     ABCA

效率+准确KMP:正确姿势

ABCABCABD
   ABCABD

下面考虑这个正确又有效率的跳法原理:

ABCABD
当A对不上:不用说 往后移一位 

当B对不上:说明target前面一位是A 当前位不是B 那得看看当前位是不是A吧 所以往后移一位

当C对不上:target的前面两位是AB  当前位不是C  那肯定不用移一位了 都说了target的上一个是B 干嘛还比呢 得移两位

当A对不上(第二个A):target的前面三位是ABC  当前位不是A  真是好可惜 移3位

当B对不上(第二个B):target的前面四位是ABCA  当前位不是B  真是好可惜 你看这都不是B了 移3位也没用 直接移4位看看是不是A吧

当D对不上:target的前面五位是ABCAB  当前位不是D  这时候 看好了 移3位~~!! 因为当前不是D他可能是C啊,得给第二个AB一个尝试的机会吧,所以移3位

很混乱是不是??到底移几位??怎么移??所以得给这个字符串专门生成一个数组 数组存储每个位置出错了分别要移几位 记下来就好了嘛!

怎么生成?就是前面思考时的规则,下面详细分析一下这个混乱的规则:

为什么D的时候要移3位?因为它前面的AB刚好和pattern的AB一样!(我走5步欸 退2步哎)

为什么第二个B出错要移4位?因为它前面是A但是当前不是B 所以没有必要给它前面这个A机会(我走4步欸 不用退欸)

也就是说 我们就是要找:用不用退 退几步 而退几步取决于:前面这个字符串的后缀和前缀的重合长度(ABCA:1)(ABCAB:2)

啊哦,这里有个小问题,姑且认为如果第二个B出错我们也给它前面那个A一个机会,移3位吧,这里后续可以做个优化,其实不应该给的~~

好的,现在问题转化为:统计每个字符串前后缀重复长度,就是这样子:


ABCABD
000120
我懒得写了。。。

贴几个链接自己看吧>_<

推荐:http://www.cnblogs.com/c-cloud/p/3224788.html

继续:上面这个数组000120是ABCABD对应的“next”数组 因为要靠这个数组来间接决定要移几位 就是“next”头结点要移到哪里

现在问题转换为求出每个位置对应的那个退的长度 也就是“前缀后缀重复长度”

即:ABCA:1      ABCAB:2   怎么求这个next数组呢?

这又不是回味字符串 比如ABCBA求回文长度 两头掐着往中间 走就可以了  这个要求的是 ABCAB 中AB的长度  不预先知道长度就不知道第二个字符串的头结点在哪里 还是挺难受的

方法是:

1A:不用解释 0

2B:B不等于1A 所以还是0

3C:仍然不等于1A  0

4A:等于1A  所以是1

5B:4A已经确认过前4个的前后缀重复长度是1 也就是4已经告诉我他和1相等 那我就去跟2 比 相等就+1 是2 不相等就是0  5B=2B 所以是2

6D:5已经告诉我他们5个的长度是2 也就是说12和45是相等的 所以我可以直接去跟3比 相等就+1返回3 不等就直接是0   

如果还有7 那么他看到6是0 说明前6个并不满足前后缀相等 所以应该直接跟1对比 (大不了重头再来)

总结:遍历一遍即可 next[i] 去看next[i-1]的值val  然后将pattern[i] 与 pattern[val]进行对比 相等:next[i] = next[i-1]+1 不相等 next[i] = 0

至此 构造出了next数组 

然后应用next数组到前面的“退步”规则中  结束!

public class Solution {
	public static void main(String[] args) {
		Solution solution = new Solution();
		for(int i : solution.getNext("ABCDABD")){
			System.out.print(i);
		}
		System.out.println();
		System.out.println(solution.strStr("AHGBBABA", "ABA"));
	}
	//needle:pattern字符串
	//haystack:target字符串
    public int strStr(String haystack, String needle) {
    	if(needle == "" || haystack == "")
    		return 0;
    	int[] next = getNext(needle);
    	for(int i = 0;i <= haystack.length()-needle.length();){
    		int j = 0;
    		for(;j < needle.length();j++){
    			if(needle.charAt(j) != haystack.charAt(i+j)){
    				break;
    			}
    		} 
    		if(j == needle.length())
    			return i;
    		//解法1:向后移一位 万无一失 但没有体现算法 没有优化
    		//	i++;
    		//解法1结束
    		//解法2:KMP移法 
    		//     在needle(pattern)的第j位对不上了 那么就应该去next数组查应该后移几位
    		if(j == 0){
    			i++;
    		}
    		else{
    			i = i+j-next[j-1];
    		}
    		//解法2结束
    	}
    	return -1;
    }
    
    public int[] getNext(String needle){
    	int[] next = new int[needle.length()];
    	if(needle.length() > 0)
    		next[0] = 0;
    	for(int i = 1;i < needle.length();i++){
    		int index = next[i-1];
    		if(needle.charAt(i) == needle.charAt(index)){
    			next[i] = next[i-1]+1;
    		}
    		else{
    			next[i] = 0;
    		}
    	}
    	return next;
    }
}





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值