1576. 替换所有的问号
难度:打卡
考点:遍历
时间复杂度:O(n)
class Solution {
//将字符串存入字符数组,注意首尾多开一个设置为0
//然后每次遇到‘?’我们就将其变为与前后都不同的字符即可
public String modifyString(String s) {
char[] cha = new char[26];
for(int i = 0; i < 26; ++i){
cha[i] = (char)('a' + i);
}
char[] chs = new char[110];
int n = s.length();
chs[0] = '0';
for(int i = 1; i <= n; ++i){
chs[i] = s.charAt(i - 1);
}
chs[n + 1] = '0';
for(int i = 1; i <= n; ++i){
int c = 0;
if(chs[i] == '?'){
while(cha[c] == chs[i - 1] || cha[c] == chs[i + 1]){
++c;
}
chs[i] = cha[c];
}
}
String str = "";
for(int i = 1; i <= n; ++i){
str += chs[i];
}
return str;
}
}
1577. 数的平方等于两数乘积的方法数
难度:打卡
考点:哈希表
时间复杂度:O(n^2)
//哈希表加速即可
class Solution {
HashMapmap2 = new HashMap<>();
HashMapmap1 = new HashMap<>();
public int numTriplets(int[] nums1, int[] nums2) {
int res = 0;
for(int i = 0; i < nums2.length; ++i){
for(int j = i + 1; j < nums2.length; ++j){
long cur = (long)nums2[i] * nums2[j];
map2.put(cur, map2.getOrDefault(cur, 0) + 1);
}
}
for(int i = 0; i < nums1.length; ++i){
long cur = (long)nums1[i] * nums1[i];
if(map2.containsKey(cur)){
res += map2.get(cur);
}
}
for(int i = 0; i < nums1.length; ++i){
for(int j = i + 1; j < nums1.length; ++j){
long cur = (long)nums1[i] * nums1[j];
map1.put(cur, map1.getOrDefault(cur, 0) + 1);
}
}
for(int i = 0; i < nums2.length; ++i){
long cur = (long)nums2[i] * nums2[i];
if(map1.containsKey(cur)){
res += map1.get(cur);
}
}
return res;
}
}
1578. 避免重复字母的最小删除成本
难度:打卡
考点:双指针,累加重复字符的权值减去最大权值
时间复杂度:O(n)
class Solution {
public int minCost(String s, int[] cost) {
int n = s.length();
if(n <= 1)return 0;
char[] chs = s.toCharArray();
int i = 0, j = 1;
int res = 0;
while(i < n && j < n){
int cur = cost[i];
int max = cost[i];
while(j < n && chs[j] == chs[i]){
cur += cost[j];
max = Math.max(max, cost[j]);
++j;
}
if(j - i > 1){
res += cur - max;
}
i = j;
j++;
}
return res;
}
}
1579. 保证图可完全遍历
难度:中等
考点:并查集、dfs、(克鲁斯卡尔最小生成树的方式其实是并查集)(公共边方法其实是dfs)
时间复杂度O(n + m)
算法思想:先找到3的类型,将其插入到并查集,然后在根据这个并查集分别去添加1类型和2类型,重复的边就计数,到最后所有节点都是在一个节点下的(可以循环也可以先设定n,每次加入一条边就减一表示并查集的节点个数加1),否则就是非连通图,输出结果即可。
并查集做法:
可以先设置联通的点为n,每次联通边添加进并查集则n--即可
class Solution {
int[] pa = new int[100010];
int[] pb = new int[100010];
int find(int x,int[] p){
if(x != p[x])p[x] = find(p[x], p);
return p[x];
}
boolean add(int a,int b,int[] p){
int p1 = find(a,p);
int p2 = find(b,p);
if(p1 == p2)return true;
p[p2] = p1;
return false;
}
public int maxNumEdgesToRemove(int n, int[][] edges) {
int res = 0;
int cc = n, cb = n;//优化点
for(int i = 1; i <= n; ++i){
pa[i] = i;
pb[i] = i;
}
for(int i = 0; i < edges.length; ++i){
int t = edges[i][0];
int a = edges[i][1];
int b = edges[i][2];
if(t == 3){
boolean f = add(a, b, pa);
if(f)res++;
else{
cc--;
cb--;
}
}
}
for(int i = 0; i <= n; ++i)pb[i] = pa[i];
for(int i = 0; i < edges.length; ++i){
int t = edges[i][0];
int a = edges[i][1];
int b = edges[i][2];
if(t == 1){
boolean f1 = add(a, b, pa);
if(f1)res++;
else cc--;
}
if(t == 2){
boolean f1 = add(a, b, pb);
if(f1)res++;
else cb--;
}
}
if(cc > 1 || cb > 1)return -1;//大于一是因为刚开始并查集就有一个节点,就是根节点
return res;
}
}
精妙的dfs(需要分别存储123三种类型边,然后dfs他们)
算法思想:
这道题用普通的dfs就可以解,思路如下:
首先dfs Alice 和 Bob,其中有一位不能dfs走完全部节点,则返回-1.
之后,清空数据,从1节点开始,dfs只走3类型的边,dfs完记录最多能访问多少个节点count。这些节点组成一个可用公共边互通的子集,子集内遍历访问的边数就是count-1;
再找下一个未访问的节点,继续dfs,并继续形成子集,并把所有的子集边数相加为sumBian = sum(count[i] - 1);
这个sum则是Alice和Bob遍历中,能公用的最大边数。
则Alice和Bob 访问使用的边数量为: common = (n - 1) + (n - 1) - sum
最大可删除的边为: edges.length - common = edges.length - (n - 1) * 2 + sum.
作者:huanglin
链接:https://leetcode-cn.com/problems/remove-max-number-of-edges-to-keep-graph-fully-traversable/solution/po-su-de-shen-du-bian-li-si-xiang-fu-za-du-onnwei-/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution {
int N = 100010;
boolean[] f = new boolean[N];
ArrayList[][] g = new ArrayList[3][N];
int cnt = 0;
public int maxNumEdgesToRemove(int n, int[][] edges) {
int l = edges.length;
for(int i = 0; i < 3; ++i){
for(int j = 0; j <= n; ++j)
g[i][j] = new ArrayList();
}
for(int i = 0; i < l; ++i){//建图
int t = edges[i][0];
int a = edges[i][1];
int b = edges[i][2];
g[t - 1][a].add(b);
g[t - 1][b].add(a);
}
dfs(1, 0);
if(cnt < n)return -1;
cnt = 0;
Arrays.fill(f, false);
dfs(1, 1);
if(cnt < n)return -1;
Arrays.fill(f, false);
int com = 0;
for(int i = 1; i <= n; ++i){
if(!f[i]){
cnt = 0;
dfs(i, 2);//2表示不深搜type为1和2的边只搜3的边,找出公共边的长度
com += cnt - 1;
}
}
return l - (n - 1 << 1) + com;
}
void dfs(int v, int t){
if(f[v])return;
f[v] = true;
cnt++;
for(Object i : g[2][v]){//公共:贪心的思想先深搜公共边
int c = (int)i;
if(!f[c])
dfs(c, t);
}
if(t == 0 || t == 1){//私自:深搜各自的边
for(Object i : g[t][v]){
int c = (int)i;
if(!f[c])
dfs(c, t);
}
}
}
}