3008. Find Beautiful Indices in the Given Array II

Leetcode

public List<Integer> beautifulIndices(String s, String a, String b, int k)

这题本质就是strstr + 双指针问题。

题目应该分成两个部分看。

第一个部分,是找到s里面所有a子字符串的index数组已经所有b子字符串的index数组。

第二部分就变成了,给两个排好序的整型数数组a和b,找到所有a里面的元素,能够和b里面某一个元素的差的绝对值<=k。

这题最难的部分其实就是第一个部分的strstr,根据数据量的要求,只能是kmp(或者更高级的)。kmp我参考了:字符串匹配的KMP算法 - 阮一峰的网络日志 还有 彻底搞懂KMP算法原理 | CodeTime

有一点我需要自己补充的是,虽然他们都提到了next数组本身是左右从首字符开始的子字符串的前后缀的共有元素。但是还是没有讲清楚next数组到底意味着什么和什么作用。

我举个例子:

如果需要匹配的needle是:abcabcd,这里对应的next数组就是:0001230。 我们对齐来看

abcabcd

0001230

假设扫描haystack的指针是i,扫描needle的指针是j,在匹配不上的时候,kmp的处理逻辑i不动,是 j = next[j - 1],这里的意思指的是。如果当前匹配不上,上一个可能匹配的上的是什么。这样说其实还是有一点抽象。再具体一点是这样的:

如果haystack是asadabcabcfwer

当我们走到f这个数字的时候,在haystack那边我们已经走到了最后一个d,但是d和f匹配不上,然后已知的是之前匹配过了abc了,所以j = next[j - 1] 会返回的,不是上一个abc最后的位置c,而是走完了上一次abc之后的东西。所以这里对应的位置是3而不是2(也就是上一个c的位置),而是走完了第一套abc之后的下一个a。

所以这里还涉及到的另一个问题就是,如果在字符不匹配的情况下j = next[j - 1],我找到一个完整的匹配还要接着往下找的情况下,i和j该怎么处理。

这里其实是类似的,i往下走一步,然后j对应的,其实是next最后一个元素。就是j = next[next.length - 1]。因为一般在不匹配的情况下,next最后一个元素永远是访问不到的( j = next[j - 1],  0 <= j < next.length)。但是因为匹配完了整个字符串,所以你上一个匹配到的字符,就是最后一个字符,所以对应的next的位置应该也是最后一个位置。

上面描述的就是用kmp解决第一部分所要做的。

第二部分其实就比较简单了,因为你所要找到的不是全部符合条件的配对,而只是其中一边符合条件的全部元素,所以双指针加上贪心就可以了。

给出代码如下:

class Solution {
    public List<Integer> beautifulIndices(String s, String a, String b, int k) {
        List<Integer> aMatched = this.KMPFindAll(s, a);
        List<Integer> bMatched = this.KMPFindAll(s, b);

        int bIdx = 0;
        List<Integer> result = new LinkedList<>();
        for (int aIdx = 0; aIdx < aMatched.size() && bIdx < bMatched.size(); aIdx++) {
            while (bIdx < bMatched.size() && aMatched.get(aIdx) - bMatched.get(bIdx) > k) {
                bIdx++;
            }

            if (bIdx < bMatched.size() && Math.abs(aMatched.get(aIdx) - bMatched.get(bIdx)) <= k) {
                result.add(aMatched.get(aIdx));
            }
        }

        return result;
    }

    private List<Integer> KMPFindAll(String haystack, String needle) {
        int[] next = new int[needle.length()];
        int i = 1, j = 0;
        // build up the next array
        while (i < next.length) {
            if (needle.charAt(i) == needle.charAt(j)) {
                next[i] = j + 1;
                i++;
                j++;
            } else {
                if (j == 0) {
                    i++;
                } else {
                    j = next[j - 1];
                }
            }
        }

        List<Integer> result = new ArrayList<>();
        // Use next array to build result.
        i = 0;
        j = 0;
        while (i < haystack.length()) {
            if (haystack.charAt(i) == needle.charAt(j)) {
                i++;
                j++;
                if (j == needle.length()) {
                    result.add(i - j);
                    j = next[j - 1];
                }
            } else {
                if (j != 0) {
                    j = next[j - 1];
                } else {
                    i++;
                }
            }
        }

        return result;
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值