Leetcode刷题 2021.01.31
Leetcode839 相似字符串组
如果交换字符串 X 中的两个不同位置的字母,使得它和字符串 Y 相等,那么称 X 和 Y 两个字符串相似。如果这两个字符串本身是相等的,那它们也是相似的。
例如,“tars” 和 “rats” 是相似的 (交换 0 与 2 的位置); “rats” 和 “arts” 也是相似的,但是 “star” 不与 “tars”,“rats”,或 “arts” 相似。
总之,它们通过相似性形成了两个关联组:{“tars”, “rats”, “arts”} 和 {“star”}。注意,“tars” 和 “arts” 是在同一组中,即使它们并不相似。形式上,对每个组而言,要确定一个单词在组中,只需要这个词和该组中至少一个单词相似。
给你一个字符串列表 strs。列表中的每个字符串都是 strs 中其它所有字符串的一个字母异位词。请问 strs 中有多少个相似字符串组?
没有看题解就做出来的每日一题的困难题,其实也没有那么难。就是并查集加判断字符串而已,最后返回连通分量的数目。一开始觉得判断字符串那里可能会超时,没想到直接就过了。
class Solution {
public int numSimilarGroups(String[] strs) {
int n = strs.length;
UnionFind uf = new UnionFind(n);
for(int i = 0; i < n; i++){
for(int j = i + 1; j < n; j++){
if (helper(strs[i], strs[j])){
uf.union(i, j);
}
}
}
return uf.size;
}
//判断两个字符串是否交换一次就能相似
private boolean helper(String s1, String s2){
char[] arr1 = s1.toCharArray();
char[] arr2 = s2.toCharArray();
char c1 = 'A', c2 = 'B', k = 1;
for(int i = 0; i < arr1.length; i++){
if (arr1[i] == arr2[i]){
continue;
}else{
if (--k < 0) return false;
if (c1 == 'A'){
c1 = arr2[i];
c2 = arr1[i];
}else{
if (arr1[i] != c1 || arr2[i] != c2) return false;
}
}
}
return true;
}
//并查集
class UnionFind{
int[] parent;
int size;
public UnionFind(int n){
this.size = n;
parent = new int[n];
for(int i = 0; i < n; i++){
parent[i] = i;
}
}
public int find(int i){
if (parent[i] == i) return i;
return parent[i] = find(parent[i]);
}
public void union(int i, int j){
int root1 = find(i);
int root2 = find(j);
if (root1 == root2) return;
parent[root1] = root2;
size--;
}
}
}
Leetcode1209 删除字符串中的所有相邻重复项 II
给你一个字符串 s,「k 倍重复项删除操作」将会从 s 中选择 k 个相邻且相等的字母,并删除它们,使被删去的字符串的左侧和右侧连在一起。
你需要对 s 重复进行无限次这样的删除操作,直到无法继续为止。
在执行完所有删除操作后,返回最终得到的字符串。
本题答案保证唯一。
看到这种类似于消消乐的题目,首先就是想到用栈进行模拟了。遇到三个字符就消除,但是这里可能会有两个字符中间隔着其他字符,再遇到一个字符,所以还需要一个变量记录目前相等的字符的个数。所以另外再用一个类记录。
class Solution {
public String removeDuplicates(String s, int k) {
char[] arr = s.toCharArray();
Deque<Ele> stack = new ArrayDeque<>();
for(int i = 0; i < arr.length; i++){
//如果栈为空,或者栈顶元素不相同,就放进1
if (stack.isEmpty() || stack.peek().c != arr[i]) {
stack.push(new Ele(arr[i], 1));
}else{
//相同的话,就在原来的基础上加1
stack.push(new Ele(arr[i], stack.peek().num + 1));
//相同数目到k个就全都pop出去
if (stack.peek().num == k){
for(int j = 0; j < k; j++) stack.pop();
}
}
}
//用StringBuilder返回
StringBuilder sb = new StringBuilder();
for(Ele ele : stack){
sb.append(ele.c);
}
return sb.reverse().toString();
}
//新的类,保存字符和连续相等的字符数目
class Ele{
char c;
int num;
public Ele(char c, int num){
this.c = c;
this.num = num;
}
}
}
Leetcode1208 尽可能使字符串相等
给你两个长度相同的字符串,s 和 t。
将 s 中的第 i 个字符变到 t 中的第 i 个字符需要 |s[i] - t[i]| 的开销(开销可能为 0),也就是两个字符的 ASCII 码值的差的绝对值。
用于变更字符串的最大预算是 maxCost。在转化字符串时,总开销应当小于等于该预算,这也意味着字符串的转化可能是不完全的。
如果你可以将 s 的子字符串转化为它在 t 中对应的子字符串,则返回可以转化的最大长度。
如果 s 中没有子字符串可以转化成 t 中对应的子字符串,则返回 0。
这道题还是容易想到的,基本的滑动窗口而已,好像没什么可说的。作为基本题,应该熟练掌握。
class Solution {
public int equalSubstring(String s, String t, int maxCost) {
char[] arr1 = s.toCharArray();
char[] arr2 = t.toCharArray();
int res = 0, i = 0, j = 0, n = arr1.length, sum = 0;
//滑动窗口过程中更新全局最大值
while (j < n){
sum += Math.abs(arr1[j] - arr2[j]);
j++;
while (sum > maxCost){
sum -= Math.abs(arr1[i] - arr2[i]);
i++;
}
res = Math.max(res, j - i);
}
return res;
}
}