LeetCode第205场周赛
A:替换所有的问号
题意
给定一个只含小写字母和?的字符串,现需要将所有的问号改为小写字母,切不能和与其相连的字符相同。
题解
直接暴力即可。遍历整个字符串,出现?号判断即可。
AC代码(cpp)
class Solution {
public:
string modifyString(string s) {
int len=s.length();
for(int i=0;i<len;i++){
if(s[i]=='?'){
if(i==0){
for(int j=0;j<26;j++){
if('a'+j!=s[i+1]){
s[i]='a'+j;break;
}
}
}else if(i==len-1){
for(int j=0;j<26;j++){
if('a'+j!=s[i-1]){
s[i]='a'+j;break;
}
}
}else{
for(int j=0;j<26;j++){
if('a'+j!=s[i-1]&&'a'+j!=s[i+1]){
s[i]='a'+j;break;
}
}
}
}
}
return s;
}
};
比赛时写的,代码有点丑
B:数的平方等于两数乘积的方法数
题意
给定两个整形数组num1、num2,需要求三元组的个数
三元组规则如下:
- n u m 1 [ i ] 2 = n u m 2 [ j ] ∗ n u m 2 [ k ] ; j < k num1[i]^2=num2[j]*num2[k];j<k num1[i]2=num2[j]∗num2[k];j<k
- n u m 2 [ i ] 2 = n u m 1 [ j ] ∗ n u m 1 [ k ] ; j < k num2[i]^2=num1[j]*num1[k];j<k num2[i]2=num1[j]∗num1[k];j<k
题解
数据为1000,显然O(
n
3
n^3
n3)是不行的,于是考虑O(
n
2
n^2
n2)
直接使用hash表(个人习惯于c++,于是即:c++中的map or unordered_map)保存两个不同的数的乘积。然后遍历一遍另外一个数组判断是否有元素的平方与两个数的乘积相同的情况,具体见代码。
AC代码(cpp)
#define ll long long
class Solution {
public:
int solve(vector<int>& a, vector<int>& b){
map<ll,int>mp;
int ans=0;
int len1=a.size(),len2=b.size();
for(int j=0;j<len2;j++){
for(int k=j+1;k<len2;k++){
mp[(ll)b[j]*b[k]]++;
}
}
for(int i=0;i<len1;i++){
ans+=mp[(ll)a[i]*a[i]];
}
return ans;
}
int numTriplets(vector<int>& nums1, vector<int>& nums2) {
return solve(nums1,nums2)+solve(nums2,nums1);
}
};
C:避免重复字母的最小删除成本
题意
给定一字符串s,和一个对应的代价数组(即:删除s[i]则对应需要付出代码cost[i])
现需要相邻的字符不相同,问最少需要多少删除代价,才能满足题意。
题解
dp。dp[i][j]表示:前i个字符满足题意的条件下最后的字符为’a’+j的情况。
于是乎状态转移方程为:
- 删 除 s [ i ] 的 情 况 : d p [ i ] [ j ] = d p [ i − 1 ] [ j ] + c o s t [ i ] 删除s[i]的情况:dp[i][j]=dp[i-1][j]+cost[i] 删除s[i]的情况:dp[i][j]=dp[i−1][j]+cost[i]
- 不 删 除 s [ i ] 的 情 况 : d p [ i ] [ j ] = d p [ i − 1 ] [ k ] ; 其 中 k 不 等 于 j 不删除s[i]的情况:dp[i][j]=dp[i-1][k];其中k不等于j 不删除s[i]的情况:dp[i][j]=dp[i−1][k];其中k不等于j
AC代码(cpp)
#define inf 0x3f3f3f3f
class Solution {
public:
int dp[100010][30]; //dp[i][j]:前i个字符删除操作结束后最后的字符为j+'a'的最少代价(需要注意dp[i][26]表示前i个字符都需要删除的情况)
int minCost(string s, vector<int>& cost) {
int n=s.length();
memset(dp,inf,sizeof(dp));
dp[0][s[0]-'a']=0;dp[0][26]=cost[0];
for(int i=1;i<n;i++){
//删除s[i]的情况
for(int j=0;j<=26;j++){
dp[i][j]=dp[i-1][j]+cost[i];
}
//不删除s[i]的情况
int j=s[i]-'a';
for(int k=0;k<=26;k++){
if(k==j) continue;
dp[i][j]=min(dp[i][j],dp[i-1][k]);
}
}
int ans=inf;
for(int i=0;i<=26;i++){
ans=min(ans,dp[n-1][i]);
}
return ans;
}
};
D:保证图可完全遍历
题意
给定一无向图,有三种类型的边:分别为Alice可以走,Bob可以走,两个都可以走。问最多删除多少条边可以使得Alice和Bob都可以遍历所有的点。
题解
问最多删除多少条边,转化为最少需要多少条边可以使得图对于Alice和Bob而言都是联通的。也就是:最小生成树问题。
先考虑第三种类型的边,然后考虑另外两种类型的边,具体操作间代码。
AC代码(cpp)
class Solution {
public:
int para[100010],parb[100010];
int get_par1(int a){
if(para[a]!=a) para[a]=get_par1(para[a]);
return para[a];
}
int get_par2(int a){
if(parb[a]!=a) parb[a]=get_par2(parb[a]);
return parb[a];
}
int maxNumEdgesToRemove(int n, vector<vector<int>>& edges) {
for(int i=1;i<=n;i++) para[i]=i,parb[i]=i;
int ans=0,cnta=n,cntb=n; //ans表示生成的需要多少条边,cnta:表示Alice的连通块为多少,cntb:表示Bob的连通块为多少
//考虑类型3的边
for(int i=0;i<edges.size();i++){
int c=edges[i][0],u=edges[i][1],v=edges[i][2];
if(c==3){
int para_u=get_par1(u),para_v=get_par1(v);
int parb_u=get_par2(u),parb_v=get_par2(v);
if(para_u!=para_v){
para[para_v]=para_u;cnta--;
parb[parb_v]=parb_u;cntb--;
}else ans++;
}
}
//考虑另外两种类型的边
for(int i=0;i<edges.size();i++){
int c=edges[i][0],u=edges[i][1],v=edges[i][2];
if(c==3) continue;
if(c==1){
int para_u=get_par1(u),para_v=get_par1(v);
if(para_u!=para_v){
para[para_v]=para_u;cnta--;
}else ans++;
}else{
int parb_u=get_par2(u),parb_v=get_par2(v);
if(parb_u!=parb_v){
parb[parb_v]=parb_u;cntb--;
}else ans++;
}
}
if(cnta>1||cntb>1) return -1;
return ans;
}
};