28. Implement strStr()

题目

Return the index of the first occurrence of needle in haystack, or -1 if needle is not part of haystack.

Example:

Input: haystack = “hello”, needle = “ll”
Output: 2

What should we return when needle is an empty string? This is a great question to ask during an interview.

For the purpose of this problem, we will return 0 when needle is an empty string. This is consistent to C’s strstr() and Java’s indexOf().

我的想法

方法一:双指针
这双指针基本等于brute force了,最坏情况的复杂度O(m*n)

class Solution {
    public int strStr(String haystack, String needle) {
        if(needle.length() == 0) return 0;
        if(haystack.length() == 0) return -1;
        
        int i = 0;
        while(i < haystack.length()){
            if(haystack.charAt(i) != needle.charAt(0)) {i++; continue;}
            else{
                boolean flag = true;
                for(int m=i,n=0; n<needle.length(); m++,n++){
                    if(m >= haystack.length() || haystack.charAt(m) != needle.charAt(n)){
                        flag = false; break;
                    }
                }
                if(flag) return i;
                else {i++; continue;}
            }
        }
        return -1;
    }
}

2019.9.14update:以前的双指针写的太蠢了。。。其实一个循环就足够了。
这次做的时候有好几个方面没有考虑到:

  1. 只考虑到了source的异常值,而忽略了target也存在异常值判断
  2. 理所因当的觉得不会有重叠的部分,即认为都是“abcdcb”“dcb”这样,不符合直接往后移就好了。忽略了"mississippi" "issip"这种需要退回的情况
  3. 在写退回的时候走了弯路,直接在外面多加一层外层循环。这样既费时间,又忽略了题目要求的是返回第一次出现的index。在"aabaabbabaaab" "abaa"这种情况下,会返回j = 0时的index = 7,而实际上题目要的是index = 1
        for(int j = 0; j < source.length(); j++){
            int end = 0;
            for(int i = j; i < source.length(); i++) {
                if(source.charAt(i) != target.charAt(end)) {
                    end = 0;
                    continue;
                }
                end++;
                if(end == target.length()) return i - end + 1;
            }
        }

因为前面的字符已经判断过,所以只需要退回到已判断的target的长度即可
最后Accept的写法

class Solution {
    public int strStr(String source, String target) {
        if(source == null || source.length() < target.length()) return -1;
        if(target == null || target.length() == 0) return 0;
        int end = 0;
        for(int i = 0; i < source.length(); i++) {
            if(source.charAt(i) != target.charAt(end)) {
                i = i - end; //退回
                end = 0;
                continue;
            }
            end++;
            if(end == target.length()) return i - end + 1;
        }

        return (source.length() == 0) ? 0 : -1;
    }
}

方法二:队列
性能提升了很多,不知道equals()的复杂度要不要算进去,如果不算的话复杂度就是线性了

class Solution {
    public int strStr(String haystack, String needle) {
        if(needle.length() == 0) return 0;
        if(haystack.length() == 0 || haystack.length()<needle.length()) return -1;
        
        LinkedList<Character> listN = new LinkedList<>();
        LinkedList<Character> listH = new LinkedList<>();
        
        int lengthN = needle.length();
        
        for(int i = 0; i < lengthN; i++){
            listN.add(needle.charAt(i));
            listH.add(haystack.charAt(i));
        }
        if(listH.equals(listN)) return 0;
        
        for(int i = 1; i < haystack.length()-lengthN+1; i++){
            listH.pollFirst();
            listH.addLast(haystack.charAt(i+lengthN-1));
            if(listH.equals(listN)) return i;
        }
        return -1;
    }
}

解答

leetcode solution 1:substring
思想和用队列是一样的,但是这里直接用的substring函数,没有额外用数据结构了
2019.9.14update:这个方法很妙!!target的长度和内容是固定的,只要循环开头提取子串,再进行判断即可

class Solution {
    public int strStr(String haystack, String needle) {
        if(needle.length() == 0) {
            return 0;
        }
        for(int i = 0; i < haystack.length() - needle.length() + 1; i++) {
            if(haystack.substring(i, i + needle.length()).equals(needle)) {
                return i;
            }
        }
        return -1;
    }
}

2019.9.16update:KMP实用性不高,这里需要记住的是另一个算法Rabin Karp Algorithm
这个算法通过比较字符串的Hash值来初步判断两字符串是否相等

  1. 计算target的Hash值:"abbc" = a*31^3 + b*31^2 + b*31^1 + c*31^0
  2. 滚动计算source子串的Hash值:
    设"anbcdd"中"anbc"的hash值为x,计算下一子串"nbcd"时无需重新计算,只 需去掉a的值加入d即可"nbcd" = (x - a*31^3)*31 + d*31^0
class Solution {
    public int strStr(String source, String target) {
        if(source == null || target == null) return -1;
        if(target.length() == 0) return 0;

        int BASE = 1000000;
        int len = target.length();
        
        //power
        int power = 1;
        for(int i = 0; i < len; i++) {
            power = (power * 31) % BASE;
        }
        
        //target
        int targetCode = 0;
        for(int i = 0; i < len; i++) {
            targetCode = (targetCode * 31 + target.charAt(i)) % BASE;
        }
        
        //source
        int sourceCode = 0;
        for(int i = 0; i < source.length(); i++) {
            sourceCode = (sourceCode * 31 + source.charAt(i)) % BASE;
            if(i < len - 1) {
                continue;
            }
            
            //abc de --> a bcd e
            if(i >= len) {
                sourceCode = sourceCode - source.charAt(i - len) * power % BASE;
                if(sourceCode < 0) {
                    sourceCode += BASE;
                }
            }
            
            if(sourceCode == targetCode) {
                if(source.substring(i - len + 1, i + 1).equals(target))
                    return i - len + 1;
            }
        }
        return -1;
    }
}

leetcode solution 2:KMP
KMP算法介绍

class Solution {
    public int strStr(String haystack, String needle) {
        if (haystack == null || needle == null || haystack.length() < needle.length())
            return -1;
        else if (needle.length() == 0)
            return 0;
        else 
            return KMP(haystack.toCharArray(), needle.toCharArray());
    }
    
    public int KMP(char[] source, char[] target) {
        int[] next = next(target);
        
        int matchLength = 0;    
        int sourceStart = 0;    
        // i: start index of source in the match
        // j: start index of target in the match
        int i = 0, j = 0;   
        while (sourceStart <= source.length - target.length) {
            while (i < source.length && j < target.length && source[i] == target[j]) {
                matchLength++;
                i++;
                j++;
            }
            if (matchLength == target.length)
                return i - target.length;
            else if (matchLength == 0 || matchLength == 1){
                sourceStart++;
                i = sourceStart;
                j = 0;
                matchLength = 0;
            } else {
                sourceStart = i - next[j - 1];
                matchLength = next[j - 1];
                j = next[j - 1];
            }
        }
        return -1;
    }
    
    /*
     * Build next array 
     */
    public int[] next(char[] target) {
        int[] next = new int[target.length];
        for (int i = 1; i < next.length; i++) {
            if (target[i] == target[next[i - 1]])
                next[i] = next[i - 1] + 1;
            else if (target[i] == target[0]) {
                for (int k = 0; k < i; k ++) {
                	if (target[k] == target[i - k]) {
                		next[i]++;
                	} else 
                		break;
                }
            }
            else
                next[i] = 0;
        }
        return next;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本火锅店点餐系统采用Java语言和Vue技术,框架采用SSM,搭配Mysql数据库,运行在Idea里,采用小程序模式。本火锅店点餐系统提供管理员、用户两种角色的服务。总的功能包括菜品的查询、菜品的购买、餐桌预定和订单管理。本系统可以帮助管理员更新菜品信息和管理订单信息,帮助用户实现在线的点餐方式,并可以实现餐桌预定。本系统采用成熟技术开发可以完成点餐管理的相关工作。 本系统的功能围绕用户、管理员两种权限设计。根据不同权限的不同需求设计出更符合用户要求的功能。本系统中管理员主要负责审核管理用户,发布分享新的菜品,审核用户的订餐信息和餐桌预定信息等,用户可以对需要的菜品进行购买、预定餐桌等。用户可以管理个人资料、查询菜品、在线点餐和预定餐桌、管理订单等,用户的个人资料是由管理员添加用户资料时产生,用户的订单内容由用户在购买菜品时产生,用户预定信息由用户在预定餐桌操作时产生。 本系统的功能设计为管理员、用户两部分。管理员为菜品管理、菜品分类管理、用户管理、订单管理等,用户的功能为查询菜品,在线点餐、预定餐桌、管理个人信息等。 管理员负责用户信息的删除和管理,用户的姓名和手机号都可以由管理员在此功能里看到。管理员可以对菜品的信息进行管理、审核。本功能可以实现菜品的定时更新和审核管理。本功能包括查询餐桌,也可以发布新的餐桌信息。管理员可以查询已预定的餐桌,并进行审核。管理员可以管理公告和系统的轮播图,可以安排活动。管理员可以对个人的资料进行修改和管理,管理员还可以在本功能里修改密码。管理员可以查询用户的订单,并完成菜品的安排。 当用户登录进系统后可以修改自己的资料,可以使自己信息的保持正确性。还可以修改密码。用户可以浏览所有的菜品,可以查看详细的菜品内容,也可以进行菜品的点餐。在本功能里用户可以进行点餐。用户可以浏览没有预定出去的餐桌,选择合适的餐桌可以进行预定。用户可以管理购物车里的菜品。用户可以管理自己的订单,在订单管理界面里也可以进行查询操作。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值