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;
}
}