算法学习:leetcode28. 实现 strStr()

实现 类似Java中indexOf() 函数。

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

示例 1:

输入: haystack = "hello", needle = "ll"
输出: 2

说明:

当 needle 是空字符串时,我们应当返回什么值呢?这是一个在面试中很好的问题。

对于本题而言,当 needle 是空字符串时我们应当返回 0 。这与C语言的 strstr() 以及 Java的 indexOf() 定义相符。

思路一:截串比较,每次从haystack中substring(i,needle.length)和needle比较,知道发现相等,返回下标i

/**
     * 子串匹配
     * @param haystack
     * @param needle
     * @return
     */
    public static int strStr(String haystack, String needle) {
        int L = needle.length(), n = haystack.length();

        for (int start = 0; start < n - L + 1; ++start) {
            if (haystack.substring(start, start + L).equals(needle)) {
                return start;
            }
        }
        return -1;
    }

上面的代码是非常精简的,如果思路不好理解,可以参考第二个版本

/**
     * 子串匹配
     * @param haystack
     * @param needle
     * @return
     */
    public static int strStr(String haystack, String needle) {
        if (needle == null ||  "".equals(needle)) {
            return 0;
        }
        //如果haystack长度小于needle,那肯定不匹配
        if(needle.length()>haystack.length()) {
            return -1;
        }
        for(int i=0;i<haystack.length();i++) {
            //如果截取末位超过字符串长度,说明没有子串
            if(i+needle.length()>haystack.length()) {
                return -1;
            }
            //判断子串是否匹配
            if(haystack.substring(i,i+needle.length()).equals(needle)) {
                return i;
            }
        }
        return -1;
    }

思路二:用回溯查找,进行子串匹配。上面的子串查找的缺点是每次向后移动一个位置,截取子串开始匹配,这里的思路是将两个字符串拆开,按照字符比较,从开头字符开始比较,不相等则后移,继续比较,这里为什么叫回溯,因为如果比较到两个字符串中间位置,发现不匹配了,则要往回退,退到本次比较的头字符的下一个位置,重新开始逐个字符比较。

/**
     * 回溯查找
     * 上面的子串查找的缺点是每次向后移动一个位置,截取子串开始匹配
     * 这里的思路是将两个字符串拆开,按照字符比较,从开头字符开始比较,不相等则后移,继续比较
     * 这里为什么叫回溯,因为如果比较到两个字符串中间位置,发现不匹配了,则要往回退,
     * 退到本次比较的头字符的下一个位置,重新开始逐个字符比较
     * 例如:haystack="aabsikm",needle="absi"
     * a a b s i k m
     * | |
     * a b s i
     * 第一次比较从aabsikm的下标为0的位置开始,检测到第二个字符,发现不匹配,此时aabsikm指针向后移一位
     *
     * a a b s i k m
     *   | | | |
     *   a b s i
     *
     * @param haystack
     * @param needle
     * @return
     */
    public static int strStr3(String haystack, String needle) {
        int L = needle.length(), n = haystack.length();
        if(L == 0) {
            return 0;
        }
        int pn = 0;
        while(pn < n-L+1) {
            //判断起始字符串是否相等,不相等则haystack指针后移一位,继续比较
            while(pn < n-L+1 && haystack.charAt(pn) != needle.charAt(0)) pn++;

            //有相等的起始字符,则开始比较后续字符是否相等
            int curnLen=0, pL=0;
            while(pn < n && pL<L && haystack.charAt(pn) == needle.charAt(pL)) {
                pn++;
                pL++;
                //curnLen作用就是用作最后计算字符串是否匹配,
                // 如果curnLen长度等于needle的长度,
                // 说明匹配到了最后,也就存在子串
                curnLen++;
            }
            if(curnLen == L) {
                return pn-L;
            }
            //如果上面的while循环(while(pn < n-L+1 && haystack.charAt(pn) == needle.charAt(pL)))发现不匹配,
            // 这时候就要回退了,继续比较
            pn = pn - curnLen + 1;
        }
        return -1;
    }

除了上面两种,还有多种方法来处理这个问题,比如KMP算法,只是这种算法不是太好理解,如果要讲解,要用单独的篇幅来讲,在这里暂时先不展开,有兴趣的可以试一试。

还有一种妖娆的写法,时间效率超过了leetcode平台100%的用户

public static int strStr4(String haystack, String needle) {
        return haystack.indexOf(needle);
    }

O(∩_∩)O哈哈~