LeetCode Weekly 156

LeetCode Weekly 156

5205.独一无二的出现次数

给你一个整数数组 arr,请你帮忙统计数组中每个数的出现次数。

如果每个数的出现次数都是独一无二的,就返回 true;否则返回 false。

示例 1:

输入:arr = [1,2,2,1,1,3]
输出:true
解释:在该数组中,1 出现了 3 次,2 出现了 2 次,3 只出现了 1 次。没有两个数的出现次数相同。

示例 2:

输入:arr = [1,2]
输出:false

示例 3:

输入:arr = [-3,0,1,-3,1,1,1,-3,10,0]
输出:true

提示:

1 <= arr.length <= 1000
-1000 <= arr[i] <= 100

很简单的问题而且数据范围也很小,直接用数组记出现的数字然后再判重

int num[2001];
bool vised[2001];
class Solution {
public:
    bool uniqueOccurrences(vector<int>& arr)
    {
        memset(num,0,sizeof(num));
        memset(vised,0,sizeod(vised));
        int len=arr.size();
        for(int i=0;i<len;i++)
        {
            int tmp=arr[i];
            if(tmp<0)
            {
                tmp*=-1;
                tmp+=1000;
            }//将数字简单映射一下
            num[tmp]++;
            vised[tmp]=1;
        }
        for(int i=0;i<=2000;i++)
        {
            if(vised[i])
            {
                for(int j=0;j<=2000;j++)
                {
                    if(i!=j)
                    {
                        if(num[i]==num[j])
                        {
                            return false;
                        }
                    }
                }
            }
        }
        return true;
    }
};

5207. 尽可能使字符串相等

给你两个长度相同的字符串,s 和 t。

将 s 中的第 i 个字符变到 t 中的第 i 个字符需要 |s[i] - t[i]| 的开销(开销可能为 0),也就是两个字符的 ASCII 码值的差的绝对值。

用于变更字符串的最大预算是 maxCost。在转化字符串时,总开销应当小于等于该预算,这也意味着字符串的转化可能是不完全的。

如果你可以将 s 的子字符串转化为它在 t 中对应的子字符串,则返回可以转化的最大长度。

如果 s 中没有子字符串可以转化成 t 中对应的子字符串,则返回 0。

示例 1:

输入:s = "abcd", t = "bcdf", cost = 3
输出:3
解释:s 中的 "abc" 可以变为 "bcd"。开销为 3,所以最大长度为 3。

遇到这种区间费用问题一般用前缀数组可以使操作更加简单,记cost(i)为前i个字符的开销,这样cost(j+1)-cost(i)就是j到i这一区间的开销。同时用一个指针指向最大段的开端,每当费用超过时就将指针前移直到小于等于maxCost
再不断更新更大的答案就行了,最坏时间复杂度为O(n^2),但数据似乎没有这么毒瘤。。。更正:是O(n)哒!

long long cost[100001];
class Solution {
public:
    int equalSubstring(string s, string t, int maxCost)
    {
        memset(cost,0,sizeof(cost));
        int len=s.size();
        int maxlen=0;
        int pl=1;
        for(int i=0;i<len;i++)
        {
            cost[i+1]=abs(s[i]-t[i])+cost[i];
            if(cost[i+1]-cost[pl]<=maxCost)
            {
                maxlen=max(maxlen,i-pl+2);
            }
            else
            {
                while(cost[i+1]-cost[pl]>maxCost && pl<=i+1)
                {
                    pl++;
                }
            }
        }
        return maxlen;
    }
};

5206.删除字符串中的所有相邻重复项 II

给你一个字符串 s,「k 倍重复项删除操作」将会从 s 中选择 k 个相邻且相等的字母,并删除它们,使被删去的字符串的左侧和右侧连在一起。

你需要对 s 重复进行无限次这样的删除操作,直到无法继续为止。

在执行完所有删除操作后,返回最终得到的字符串。

本题答案保证唯一。

示例 1:

输入:s = "abcd", k = 2
输出:"abcd"
解释:没有要删除的内容。

示例 2:

输入:s = "deeedbbcccbdaa", k = 3
输出:"aa"
解释:
先删除 "eee" 和 "ccc",得到 "ddbbbdaa"
再删除 "bbb",得到 "dddaa"
最后删除 "ddd",得到 "aa"

直接上栈,相当于在线的模拟每个字符入栈的操作,同时记录下相同字母的数量。每次进栈前检查一下是否达到k,是的话就将栈顶前k个弹出。虽然不难想但是有点难写。

stack<pair<char,int> > sta;
char tmp[100001];
class Solution {
public:
    string removeDuplicates(string s, int k)
    {
        for(auto i:s)//依次枚举s中的字符
        {
            if(sta.empty())
            {
                sta.push(make_pair(i,1));
            }
            else
            {
                pair<char,int> now=sta.top();
                if(now.first==i)
                {
                    sta.push(make_pair(i,sta.top().second+1));//相同则次数+1
                }
                else
                {
                    sta.push(make_pair(i,1));
                }
            }
            while(!sta.empty() && sta.top().second==k)//检查是否达到k次
            {
                for(int j=0;j<k;j++)
                {
                    sta.pop();
                }
            }
        }
        int num=sta.size();
        tmp[num]=0;
        for(int i=num-1;i>=0;i--)
        {
            tmp[i]=sta.top().first;
            sta.pop();
        }
        string ans(tmp);
        return ans;
    }
};

5208.穿过迷宫的最少移动次数

你还记得那条风靡全球的贪吃蛇吗?

我们在一个 n*n 的网格上构建了新的迷宫地图,蛇的长度为 2,也就是说它会占去两个单元格。蛇会从左上角((0, 0) 和 (0, 1))开始移动。我们用 0 表示空单元格,用 1 表示障碍物。蛇需要移动到迷宫的右下角((n-1, n-2) 和 (n-1, n-1))。

每次移动,蛇可以这样走:

如果没有障碍,则向右移动一个单元格。并仍然保持身体的水平/竖直状态。
如果没有障碍,则向下移动一个单元格。并仍然保持身体的水平/竖直状态。
如果它处于水平状态并且其下面的两个单元都是空的,就顺时针旋转 90 度。蛇从((r, c)、(r, c+1))移动到 ((r, c)、(r+1, c))。
如果它处于竖直状态并且其右面的两个单元都是空的,就逆时针旋转 90 度。蛇从((r, c)、(r+1, c))移动到((r, c)、(r, c+1))。

返回蛇抵达目的地所需的最少移动次数。

如果无法到达目的地,请返回 -1。

示例 1:

输入:grid = [[0,0,0,0,0,1],
[1,1,0,0,1,0],
[0,0,0,0,1,1],
[0,0,1,0,1,0],
[0,1,1,0,0,0],
[0,1,1,0,0,0]]
输出:11
解释:
一种可能的解决方案是 [右, 右, 顺时针旋转, 右, 下, 下, 下, 下, 逆时针旋转, 右, 下]。

方格上的最短路,不是bfs就是dp,但这道题的状态转移很多用bfs的话码量惊人(dp的也不少)。

考虑用动态规划做,用dp(i,j,k)来表示从起点到蛇尾位于(i,j)蛇身状态为k(0表示蛇身水平,1表示蛇身竖直)时的最小移动次数。虽然题面关于走法写的很清楚,但正常人也不会想到这个蛇能平移。。。

同时因为蛇是向左下移动,因此从左向下枚举。同时蛇的转动需要2X2的方格,所以2X2的情况和非2X2的情况需要分别转移。

int dp[101][101][2];
class Solution {
public:
	int minimumMoves(vector<vector<int>>& grid)
    {
		int n=grid.size();
		memset(dp,0x3f,sizeof(dp)); 
		dp[0][0][0]=0;//初始化除0,0,0的状态其它都是无限大(非法)
		for(int i=0;i<n;i++)
        {
			for(int j=0;j<n;j++)
            {
				if(!grid[i][j])
                {
					if(i+1<n && j+1<n && !grid[i+1][j] && !grid[i][j+1] && !grid[i+1][j+1])//先考虑2X2的情况
                    {
						dp[i][j][0]=min(dp[i][j][0],dp[i][j][1]+1);
						dp[i][j][1]=min(dp[i][j][1],dp[i][j][0]+1);
						dp[i+1][j][0]=min(dp[i+1][j][0],dp[i][j][0]+1);
						dp[i][j+1][1]=min(dp[i][j+1][1],dp[i][j][1]+1);
					}
                    if(j+2<n && !grid[i][j+1] && !grid[i][j+2])
                    {
                        dp[i][j+1][0]=min(dp[i][j+1][0],dp[i][j][0]+1);
                    }
					if(i+2<n && !grid[i+1][j] && !grid[i+2][j])
                    {
                        dp[i+1][j][1]=min(dp[i+1][j][1],dp[i][j][1]+1);
                    }
				}
			}
		}
        if(dp[n-1][n-2][0]==0x3f3f3f3f)//如果结果无限大则表示没法到达
        {
            return -1;
        }
		else
        {
            return dp[n-1][n-2][0];
        }
	}
};

这场比赛的题目都是说难不难,但就是不好直接想出来的类型,这样的题目很有价值。

posted @ 2019-09-30 17:44  黑泽斯  阅读(185)  评论(0编辑  收藏  举报