前言
本文隶属于专栏《LeetCode 刷题汇总》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢!
本专栏目录结构请见LeetCode 刷题汇总
正文
幕布
26. 删除有序数组中的重复项
题解
5 lines C++/Java, nicer loops
i遍历,j占位
class Solution {
public int removeDuplicates(int[] nums) {
int i = 0;
for (int n : nums)
if (i == 0 || n > nums[i-1])
nums[i++] = n;
return i;
}
}
27. 移除元素
题解
My solution for your reference.
i遍历,j占位
class Solution {
public int removeElement(int[] nums, int val) {
int j = 0, n = nums.length;
for(int i = 0; i < n; i++) if(nums[i] != val) nums[j++] = nums[i];
return j;
}
}
28. 实现 strStr()
BF 算法(暴力解法)
题解
32 | 字符串匹配基础(上):如何借助哈希算法实现高效字符串匹配?
代码实现
class Solution {
public int strStr(String haystack, String needle) {
//i代表主串指针,j模式串
int i,j;
//主串长度和模式串长度
int halen = haystack.length();
int nelen = needle.length();
//循环条件,这里只有 i 增长
for (i = 0 , j = 0; i < halen && j < nelen; ++i) {
//相同时,则移动 j 指针
if (haystack.charAt(i) == needle.charAt(j)) {
++j;
} else {
//不匹配时,将 j 重新指向模式串的头部,将 i 本次匹配的开始位置的下一字符
i -= j;
j = 0;
}
}
//查询成功时返回索引,查询失败时返回 -1;
return j == nelen ? i - nelen : -1;
}
}
RK 算法
题解
32 | 字符串匹配基础(上):如何借助哈希算法实现高效字符串匹配?
代码实现
class Solution {
private int m, n;
private int[] power;
public int strStr(String haystack, String needle) {
int m = haystack.length(), n = needle.length();
if(n == 0) return 0;
if(m < n) return -1;
// power 表示底数为 26,指数为下标的幂
power = new int[m];
power[0] = 1;
for(int i = 1; i < m; i++){
power[i] = power[i - 1] * 26;
}
char[] s = haystack.toCharArray();
int h = hash(s, 0, n - 1);
int target = hash(needle.toCharArray(), 0 , n - 1);
if(target == h) return 0;
for(int i = 1; i < m - n + 1; i++){
h = (h - power[n - 1] * (s[i - 1] - 'a')) * 26 + (s[i + n - 1] - 'a');
if(target == h) return i;
}
return -1;
}
/**
* 计算字符数组的 hash 值,即 26 进制对应 10 进制整数
* @param c 字符数组
* @param l 左边界
* @param r 右边界
*/
private int hash(char[] c, int l, int r){
int res = 0;
for(int i = r; i >= l; i--){
res += (c[i] - 'a') * power[r - i];
}
return res;
}
}
BM算法
题解
33 | 字符串匹配基础(中):如何实现文本编辑器中的查找功能?
代码实现
public class Solution {
public int strStr(String haystack, String needle) {
if (needle.isEmpty()) {
return 0;
}
return bm(haystack.toCharArray(), haystack.length(), needle.toCharArray(), needle.length());
}
// a,b表示主串和模式串;n,m表示主串和模式串的长度。
public int bm(char[] a, int n, char[] b, int m) {
int[] bc = new int[256]; // 记录模式串中每个字符最后出现的位置
generateBC(b, m, bc); // 构建坏字符哈希表
int[] suffix = new int[m];
boolean[] prefix = new boolean[m];
generateGS(b, m, suffix, prefix);//好后缀
int i = 0; // j表示主串与模式串匹配的第一个字符
while (i <= n - m) {
int j;
for (j = m - 1; j >= 0; --j) { // 模式串从后往前匹配
if (a[i + j] != b[j]) break; // 坏字符对应模式串中的下标是j
}
if (j < 0) {
return i; // 匹配成功,返回主串与模式串第一个匹配的字符的位置
}
int x = j - bc[a[i + j]];
int y = 0;
if (j < m - 1) { // 如果有好后缀的话
y = moveByGS(j, m, suffix, prefix);
}
i = i + Math.max(x, y);
}
return -1;
}
private void generateBC(char[] b, int m, int[] bc) {
for (int i = 0; i < 256; ++i) {
bc[i] = -1; // 初始化bc
}
for (int i = 0; i < m; ++i) {
int ascii = b[i]; // 计算b[i]的ASCII值
bc[ascii] = i;
}
}
// b表示模式串,m表示长度,suffix,prefix数组事先申请好了
private void generateGS(char[] b, int m, int[] suffix, boolean[] prefix) {
for (int i = 0; i < m; ++i) { // 初始化
suffix[i] = -1;
prefix[i] = false;
}
for (int i = 0; i < m - 1; ++i) { // b[0, i]
int j = i;
int k = 0; // 公共后缀子串长度
while (j >= 0 && b[j] == b[m - 1 - k]) { // 与b[0, m-1]求公共后缀子串
--j;
++k;
suffix[k] = j + 1; //j+1表示公共后缀子串在b[0, i]中的起始下标
}
if (j == -1) prefix[k] = true; //如果公共后缀子串也是模式串的前缀子串
}
}
// j表示坏字符对应的模式串中的字符下标; m表示模式串长度
private int moveByGS(int j, int m, int[] suffix, boolean[] prefix) {
int k = m - 1 - j; // 好后缀长度
if (suffix[k] != -1) return j - suffix[k] + 1;
for (int r = j + 2; r <= m - 1; ++r) {
if (prefix[m - r]) {
return r;
}
}
return m;
}
}
KMP 算法
题解
34 | 字符串匹配基础(下):如何借助BM算法轻松理解KMP算法?
代码实现
class Solution {
public int strStr(String haystack, String needle) {
//两种特殊情况
if (needle.length() == 0) {
return 0;
}
if (haystack.length() == 0) {
return -1;
}
// char 数组
char[] hasyarr = haystack.toCharArray();
char[] nearr = needle.toCharArray();
//长度
int halen = hasyarr.length;
int nelen = nearr.length;
//返回下标
return kmp(hasyarr,halen,nearr,nelen);
}
public int kmp (char[] hasyarr, int halen, char[] nearr, int nelen) {
//获取next 数组
int[] next = next(nearr,nelen);
int j = 0;
for (int i = 0; i < halen; ++i) {
//发现不匹配的字符,然后根据 next 数组移动指针,移动到最大公共前后缀的,
//前缀的后一位,和咱们移动模式串的含义相同
while (j > 0 && hasyarr[i] != nearr[j]) {
j = next[j - 1] + 1;
//超出长度时,可以直接返回不存在
if (nelen - j + i > halen) {
return -1;
}
}
//如果相同就将指针同时后移一下,比较下个字符
if (hasyarr[i] == nearr[j]) {
++j;
}
//遍历完整个模式串,返回模式串的起点下标
if (j == nelen) {
return i - nelen + 1;
}
}
return -1;
}
public int[] next (char[] needle,int len) {
//定义 next 数组
int[] next = new int[len];
// 初始化
next[0] = -1;
int k = -1;
for (int i = 1; i < len; ++i) {
//我们此时知道了 [0,i-1]的最长前后缀,但是k+1的指向的值和i不相同时,我们则需要回溯
//因为 next[k]就时用来记录子串的最长公共前后缀的尾坐标(即长度)
//就要找 k+1前一个元素在next数组里的值,即next[k+1]
while (k != -1 && needle[k + 1] != needle[i]) {
k = next[k];
}
// 相同情况,就是 k的下一位,和 i 相同时,此时我们已经知道 [0,i-1]的最长前后缀
//然后 k - 1 又和 i 相同,最长前后缀加1,即可
if (needle[k+1] == needle[i]) {
++k;
}
next[i] = k;
}
return next;
}
}
29. 两数相除
题解
[C++/Java/Python] Should Not Use “long” Int
abs,a-(b<<x<<1)>=0
class Solution {
public int divide(int A, int B) {
if (A == 1 << 31 && B == -1) return (1 << 31) - 1;
int a = Math.abs(A), b = Math.abs(B), res = 0, x = 0;
while (a - b >= 0) {
for (x = 0; a - (b << x << 1) >= 0; x++);
res += 1 << x;
a -= b << x;
}
return (A > 0) == (B > 0) ? res : -res;
}
}
30. 串联所有单词的子串
题解
Easy Two-Map Solution (C++/Java)
两层 map
public class Solution {
public static List<Integer> findSubstring(String s, String[] words) {
List<Integer> res = new ArrayList<>();
if(s.length() == 0){
return res;
}
int sn = s.length(), wn = words.length, len = words[0].length();
Map<String, Integer> map = new HashMap<>();
for(String word : words){
map.put(word, map.getOrDefault(word, 0) + 1);
}
for(int i = 0; i < len; i++){
int count = wn, start = i;
Map<String, Integer> matchMap = new HashMap<>();
for(int j = i; j < sn; j += len){
String sub = s.substring(j, Math.min(j + len, sn));
if(map.containsKey(sub)){
matchMap.put(sub, matchMap.getOrDefault(sub, 0) + 1);
count--;
while(matchMap.get(sub) > map.get(sub)){
String remove = s.substring(start, start + len);
start += len;
matchMap.put(remove, matchMap.get(remove) - 1);
count += 1;
}
if(count == 0){
res.add(start);
}
}else{
count = wn;
start = j + len;
matchMap.clear();
}
}
}
return res;
}
}