LeetCode刷题记录

手把手带你刷Leetcode力扣|各个击破数据结构和算法|大厂面试必备技能【已完结】_哔哩哔哩_bilibili


数组

时间复杂度——访问:O(1) 搜索:O(N) 插入:O(N) 删除:O(N)

485.最大连续1的个数

给定一个二进制数组 nums , 计算其中最大连续 1 的个数。

示例 1:

输入:nums = [1,1,0,1,1,1]
输出:3
解释:开头的两位和最后的三位都是连续 1 ,所以最大连续 1 的个数是 3.

int findMaxConsecutiveOnes(int* nums, int numsSize){
    int m=0,n=0;
    for(int i=0;i<numsSize;i++){
        if(nums[i]==1)
            {
                m++;
            if(m>n)
            n=m; //n记录最大连续个数
            }
            else
            m=0;
    }
    return n;
}

283.移动零

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

请注意 ,必须在不复制数组的情况下原地对数组进行操作。

示例 1:

输入: nums = [0,1,0,3,12]
输出: [1,3,12,0,0]

void moveZeroes(int* nums, int numsSize){
	int m = 0; int n = 0;
	for (int i = 0; i < numsSize; i++){
		if (nums[i] == 0)
			m++;
		else
			nums[n++] = nums[i];//把非零数移到前面
	}
	for (int i =numsSize-1;i>numsSize-1-m; i--)
		nums[i] = 0;
	
}

链表

时间复杂度——访问:O(N) 搜索:O(N) 插入:O(1) 删除:O(1)

203.移除链表元素

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。

输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]

struct ListNode* removeElements(struct ListNode* head, int val){
    struct ListNode phead;
	phead.next = head;
	struct ListNode* p1 = &phead;
	struct ListNode* p2 = head;
	while (p2 != NULL){
		if (p2->val == val){
			p1->next = p2->next;
			p2 = p2->next;
		}
		else{
			p1 = p1->next;
			p2 = p2->next;
		}
	}
	return phead.next;
}

206.反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

struct ListNode* reverseList(struct ListNode* head){
    if (head == NULL)
		return NULL;
    struct ListNode* p1 = head;
	struct ListNode* p2 = head->next;
	while (p2 != NULL){
		p1->next = p2->next;
		p2->next = head;
		head = p2;
		p2 = p1->next;
	}
	return head;
}

队列

时间复杂度——访问:O(N) 搜索:O(N) 插入:O(1) 删除:O(1)

933.最近的请求个数

写一个 RecentCounter 类来计算特定时间范围内最近的请求。

请你实现 RecentCounter 类:

RecentCounter() 初始化计数器,请求数为 0 。
int ping(int t) 在时间 t 添加一个新请求,其中 t 表示以毫秒为单位的某个时间,并返回过去 3000 毫秒内发生的所有请求数(包括新请求)。确切地说,返回在 [t-3000, t] 内发生的请求数。
保证 每次对 ping 的调用都使用比之前更大的 t 值。

示例 1:

输入:
["RecentCounter", "ping", "ping", "ping", "ping"]
[[], [1], [100], [3001], [3002]]
输出:
[null, 1, 2, 3, 3]

解释:
RecentCounter recentCounter = new RecentCounter();
recentCounter.ping(1);     // requests = [1],范围是 [-2999,1],返回 1
recentCounter.ping(100);   // requests = [1, 100],范围是 [-2900,100],返回 2
recentCounter.ping(3001);  // requests = [1, 100, 3001],范围是 [1,3001],返回 3
recentCounter.ping(3002);  // requests = [1, 100, 3001, 3002],范围是 [2,3002],返回 3

class RecentCounter {
public:
    RecentCounter() {

    }
    
    int ping(int t) {
        q.push(t);
        while(t-q.front()>3000)
        q.pop();
        return q.size();
    }
   private:
   queue<int> q;
};

时间复杂度——访问:O(N) 搜索:O(N) 插入:O(1) 删除:O(1)

20.有效的括号

给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
每个右括号都有一个对应的相同类型的左括号。

输入:s = "(]"
输出:false

class Solution {
public:
    bool isValid(string s) {
        if(s.size()==0)
        return true;
        stack<int> st;
        for(char ch:s){
            if(ch=='('||ch=='['||ch=='{')
            st.push(ch);
            else{
                if(st.size()==0)
                return false;
                else{
                    char temp=st.top();
                    st.pop();
    if((ch==')'&&temp!='(')||(ch==']'&&temp!='[')
    ||(ch=='}'&&temp!='{'))
            return false;
                }
            }
        }
        if(st.size()==0)
            return true;
            else
            return false;
    }
};

496.下一个更大元素

nums1 中数字 x 的 下一个更大元素 是指 x 在 nums2 中对应位置 右侧 的 第一个 比 x 大的元素。

给你两个 没有重复元素 的数组 nums1 和 nums2 ,下标从 0 开始计数,其中nums1 是 nums2 的子集。

对于每个 0 <= i < nums1.length ,找出满足 nums1[i] == nums2[j] 的下标 j ,并且在 nums2 确定 nums2[j] 的 下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1 。

输入:nums1 = [4,1,2,7], nums2 = [1,4,2,5,7].
输出:[5,4,5,-1]

class Solution {
public:
	vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
		vector<int> arr;
		stack<int> st;
		for (int i = 0; i<nums2.size(); i++)
			st.push(nums2[i]);
		stack<int> temp;
		for (int i = 0; i < nums1.size(); i++){
			int max = -1;
			while (st.size() != 0){
				int top = st.top();
				st.pop();
				temp.push(top);
				if (top>nums1[i])
					max = top;
				else if (top == nums1[i])
					break;
			}
			while (temp.size() != 0){
				st.push(temp.top());
				temp.pop();
			}
			arr.push_back(max);
		}
		return arr;
	}
};

哈希表

时间复杂度—— 搜索:O(1) 插入:O(1) 删除:O(1)

217.存在重复元素

给你一个整数数组 nums 。如果任一值在数组中出现 至少两次 ,返回 true ;如果数组中每个元素互不相同,返回 false 。

输入:nums = [1,2,3,1]
输出:true

class Solution {
public:
    bool containsDuplicate(vector<int>& nums) {
        unordered_map<int,int> m;
        if(nums.size()==0)
        return false;
        for(int i=0;i<nums.size();i++){
            if(m[nums[i]]==0)
            m[nums[i]]++;
            else
            return true;
        }
        return false;
    }
};

389.找不同

给定两个字符串 s 和 t ,它们只包含小写字母。

字符串 t 由字符串 s 随机重排,然后在随机位置添加一个字母。

请找出在 t 中被添加的字母。

输入:s = "abcd", t = "abcde"
输出:"e"

class Solution {
public:
    char findTheDifference(string s, string t) {
        unordered_map<char,int>m1;
        unordered_map<char,int>m2;
        for(int i=0;i<s.size();i++)
        m1[s[i]]++;
        for(int i=0;i<t.size();i++)
        m2[t[i]]++;
        for(int i=0;i<t.size();i++)
        {
        int m=m2[t[i]]-m1[t[i]];
        if(m!=0)
        return t[i];
        }
        return 0;
    }
};

496.下一个更大的元素

nums1 中数字 x 的 下一个更大元素 是指 x 在 nums2 中对应位置 右侧 的 第一个 比 x 大的元素。

给你两个 没有重复元素 的数组 nums1 和 nums2 ,下标从 0 开始计数,其中nums1 是 nums2 的子集。

对于每个 0 <= i < nums1.length ,找出满足 nums1[i] == nums2[j] 的下标 j ,并且在 nums2 确定 nums2[j] 的 下一个更大元素 。如果不存在下一个更大元素,那么本次查询的答案是 -1 。

返回一个长度为 nums1.length 的数组 ans 作为答案,满足 ans[i] 是如上所述的 下一个更大元素.

输入:nums1 = [4,1,2], nums2 = [1,3,4,2].
输出:[-1,3,-1]

class Solution {
public:
	vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
		unordered_map<int, int>m;
		stack<int> st;
		for (int i = 0; i<nums2.size(); i++)
		{
			st.push(nums2[i]);
			if (i == nums2.size() - 1)
			{
				break;
			}
				
			if (nums2[i]>nums2[i + 1])
				continue;
			else
			{
				while (st.empty() == 0 && st.top()<nums2[i + 1])
				{
					m[st.top()] = nums2[i + 1];
					st.pop();
				}
			}
		}
		while (!st.empty()){
			m[st.top()] = -1;
			st.pop();
		}
		vector<int> arr;
		for (int i = 0; i < nums1.size(); i++)
		{
			arr.push_back(m[nums1[i]]);
		}
		return arr;
	}
};

set

特点:没有重复元素

时间复杂度—— 搜索:O(1) 插入:O(1) 删除:O(1)

217.存在重复元素

给你一个整数数组 nums 。如果任一值在数组中出现 至少两次 ,返回 true ;如果数组中每个元素互不相同,返回 false 。

class Solution {
public:
    bool containsDuplicate(vector<int>& nums) {
        set<int>st(nums.begin(),nums.end());
        if(st.size()!=nums.size())
        return true;
        else
        return false;
    }
};

705.设计哈希集合

不使用任何内建的哈希表库设计一个哈希集合(HashSet)。

实现 MyHashSet 类:

void add(key) 向哈希集合中插入值 key 。
bool contains(key) 返回哈希集合中是否存在这个值 key 。存在返回T,不存在返回F
void remove(key) 将给定值 key 从哈希集合中删除。如果哈希集合中没有这个值,什么也不做。

class MyHashSet {
private:
    vector<bool> arr;
public:
    MyHashSet() {
        arr.resize(1e6+1,false);
    }
    
    void add(int key) {
        arr[key]=true;
    }
    
    void remove(int key) {
        arr[key]=false;
    }
    
    bool contains(int key) {
        return arr[key];
    }
};

注意:满二叉树的叶子节点都必须在同一层上

时间复杂度—— 搜索:O(N) 插入:O(N) 删除:O(N)

144.树的先序遍历

class Solution {
public:
    vector<int> arr;
    vector<int> postorderTraversal(TreeNode* root) {
        if(root==NULL)
        return arr;
        arr.push_back(root->val);
        postorderTraversal(root->left);
        postorderTraversal(root->right);
        return arr;
    }
};

94.树的中序遍历

class Solution {
public:
    vector<int> arr;
    vector<int> postorderTraversal(TreeNode* root) {
        if(root==NULL)
        return arr;
        postorderTraversal(root->left);
        arr.push_back(root->val);
        postorderTraversal(root->right);
        return arr;
    }
};

145.树的后序遍历

class Solution {
public:
    vector<int> arr;
    vector<int> postorderTraversal(TreeNode* root) {
        if(root==NULL)
        return arr;
        postorderTraversal(root->left);
        postorderTraversal(root->right);
        arr.push_back(root->val);
        return arr;
    }
};

时间复杂度—— 搜索堆顶元素:O(1)- 插入:O(logN) 删除:O(logN)

215.数组中的第k个最大元素

给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。

请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。

你必须设计并实现时间复杂度为 O(n) 的算法解决此问题

输入:nums=[4,1,3,7,6,9], k=2

输出:[7]

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        priority_queue<int>q;
        for(int num:nums)
        q.push(num);
        while(k>1){
            q.pop();
            k--;
        }
        return q.top();
    }
};

 注:priority_queue <int,vector<int>,less<int>> q;    // 数组从大到小排序

        priority_queue <int,vector<int>,greater<int> > q;  // 数组从小到大排序

        priority_queue <int> q;           //默认从大到小排序

692.前k个高频单词

给定一个单词列表 words 和一个整数 k ,返回前 k 个出现次数最多的单词。

返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率, 按字典顺序 排序。

输入: words = ["i", "love", "leetcode", "i", "love", "coding"], k = 2
输出: ["i", "love"]
解析: "i" 和 "love" 为出现次数最多的两个单词,均为2次。注意,按字母顺序 "i" 在 "love" 之前。

class cmp{
public:
	bool operator()(const pair<string, int>&p1, const pair<string, int>&p2)
    {
	  return p1.second == p2.second ? p1.first<p2.first : p1.second>p2.second;
	}
};
class Solution {
public:
	vector<string> topKFrequent(vector<string>& words, int k)
	{
		unordered_map<string, int>m;
		for (string word:words)
			m[word]++;
        priority_queue <pair<string, int>,vector<pair<string, int>>, cmp>q;
		for (auto i=m.begin(); i!=m.end(); i++)
		{
			q.push(*i);
			while (q.size()>k)
				q.pop();
		}
		vector<string>arr;
		while (!q.empty())
		{
			arr.push_back(q.top().first);
			q.pop();
		}
		reverse(arr.begin(), arr.end());
		return arr;
	}
};

注:利用堆进行排序,并且堆内只能放置k个元素

 入度:多少边指向该顶点;出度:多少边从该点指向其他点。例如,Bishi有2个入度,0个出度;饲养员有1个入度,1个出度。

双指针

对撞双指针

 快慢指针

 s 和 f 相遇说明该链表是环形链表

1.两数之和

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target  的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        //先排序,再用对撞指针,最后在原数组中找到对应下标
        vector<int>ans;
        vector<int>temp;
        temp=nums;
        sort(temp.begin(),temp.end());

        int n=temp.size();
        int i=0,j=n-1;
        while(i<j)
        {
            if(temp[i]+temp[j]>target)
                j--;
            else if(temp[i]+temp[j]<target)
                i++;
            else
                break;
        }

        for(int k=0;k<n;k++)
        {
            if(i<n&&nums[k]==temp[i])
            {
                ans.push_back(k);
                i=n;
            }              
            else if(j<n&&nums[k]==temp[j])
            {
                 ans.push_back(k);
                 j=n;
            }
            if(i==n&&j==n)
                return ans;
        }
        return ans;
    }
};

141.环形链表

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

示例1:

输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。

示例2:

输入:head = [1], pos = -1
输出:false
解释:链表中没有环。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    bool hasCycle(ListNode *head) {
        if(head==NULL)
        return false;
        ListNode* s=head;
        ListNode* f=head;
        while(f!=NULL&&f->next!=NULL)
        {
            s=s->next;
            f=f->next->next;
            if(s==f)
            return true;
        }
        return false;
    }
};

881.救生艇

给定数组 people 。people[i]表示第 i 个人的体重 ,船的数量不限,每艘船可以承载的最大重量为 limit。

每艘船最多可同时载两人,但条件是这些人的重量之和最多为 limit。

返回 承载所有人所需的最小船数 。

输入:people = [3,2,2,1], limit = 3
输出:3
解释:3 艘船分别载 (1, 2), (2) 和 (3)

class Solution {
public:
    int numRescueBoats(vector<int>& people, int limit) {
        //排序后利用碰撞指针
        int result=0;
        sort(people.begin(),people.end());
        
        int n=people.size();
        int i=0,j=n-1;
        while(i<=j)
        {
            if(people[i]+people[j]<=limit)
            {
                i++;
                j--;
                result++;
            }
            else
            {
                j--;
                result++;
            }
        }
        return result;
    }
};

二分查找法

有序数列,时间复杂度为O(logN)

704. 二分查找法

给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target  ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。

输入: nums = [-1,0,3,5,9,12], target = 9
输出: 4
解释: 9 出现在 nums 中并且下标为 4

class Solution {
public:
	int search(vector<int>& nums, int target) {
		if (nums.size() == 0)
			return -1;
		int a = 0, b = nums.size() - 1;
		while (a <= b)
		{
			int mid = (b - a) / 2 + a;
			if (nums[mid] < target)
				a = mid + 1;
			else if (nums[mid] > target)
				b = mid - 1;
			else
				return mid;
		}
		return -1;
	}
};

35.搜索插入位置

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

请必须使用时间复杂度为 O(logN) 的算法。

输入: nums = [1,3,5,6], target = 2
输出: 1

class Solution {
public:
    int searchInsert(vector<int>& nums, int target) {
      if (nums.size() == 0)
			return -1;
		int a = 0, b = nums.size() - 1;
        int mid=0;
		while (a <= b)
		{
			mid = (b - a) / 2 + a;
			if (nums[mid] < target)
				a = mid + 1;
			else if (nums[mid] > target)
				b = mid - 1;
			else
				return mid;
		}
        if(nums[mid]>target)
            return mid;
        else
            return mid+1;
    }
};

162.寻找峰值

峰值元素是指其值严格大于左右相邻值的元素。

给你一个整数数组 nums,找到峰值元素并返回其索引。数组可能包含多个峰值,在这种情况下,返回 任何一个峰值 所在位置即可。

你可以假设 nums[-1] = nums[n] = -∞ 。

你必须实现时间复杂度为 O(log n) 的算法来解决此问题。

输入:nums = [1,2,1,3,5,6,4]
输出:1 或 5 
解释:你的函数可以返回索引 1,其峰值元素为 2;或者返回索引 5, 其峰值元素为 6。

class Solution {
public:
    int findPeakElement(vector<int>& nums) {
      int a=0,b=nums.size()-1;
      while(a<b)
      {
          int mid=(b-a)/2+a;
          if(nums[mid]>nums[mid+1])
            b=mid;
          else
            a=mid+1;
      }
      return a;
    }
};

 74.搜索二维矩阵

编写一个高效的算法来判断 mxn 矩阵中,是否存在一个目标值。该矩阵具有如下特性:

1、每行中的整数从左到右按升序排列。
2、每行的第一个整数大于前一行的最后一个整数。

class Solution {
public:
    bool searchMatrix(vector<vector<int>>& matrix, int target) {
        if(matrix.size()==0)
            return false;
        int r=matrix.size();
        int c=matrix[0].size();
        int a=0,b=r*c-1;
        while(a<=b)
        {
            int mid=(b-a)/2+a;
            int rr=mid/c,cc=mid%c;
            if(matrix[rr][cc]>target)
                b=mid-1;
            else if(matrix[rr][cc]<target)
                a=mid+1;
            else
                return true;
        }
        return false;
    }
};

滑动窗口

 长度最小的子数组

给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0

示例1:

输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3]是该条件下的长度最小的子数组。

示例2:

输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) {
        if(nums.size()==0)
            return 0;
        int i=0,j=0,sum=0;//i和j分别指向滑动窗口的开头和结尾
        int n=nums.size(),result=n+1;
        while(j<n)
        {
            sum+=nums[j];
            while(sum>=target)
            {
                result=min(result,j-i+1);
                sum-=nums[i];
                i++;
            }
            j++;
        }
        return result==n+1?0:result;
    }
};

1456.定长子串中元音的最大数目

给你字符串 s 和整数 k 。

请返回字符串 s 中长度为 k 的单个子字符串中可能包含的最大元音字母数。

英文中的 元音字母 为(a, e, i, o, u)。

输入:s = "abciiidef", k = 3
输出:3
解释:子字符串 "iii" 包含 3 个元音字母。

class Solution {
public:
    int maxVowels(string s, int k) {
        if(s.size()==0||s.size()<k)
            return 0;
        int i=0,sum=0,result=0;
        unordered_set<char> m={'a','e','i','o','u'};
        for(;i<k;i++)
        {
            if(m.find(s[i])!=m.end())
                sum++;
        }
        result=max(result,sum);
        i--;
        while(i<s.size())
        {
            if(m.find(s[i+1-k])!=m.end())
                sum--;
            i++;
            if(m.find(s[i])!=m.end())
                sum++;
            result=max(result,sum);
        }
        return result;
    }
};

递归

509.斐波那契数

斐波那契数 (通常用 F(n) 表示)形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:

F(0) = 0,F(1) = 1
F(n) = F(n - 1) + F(n - 2),其中 n > 1
给定 n ,请计算 F(n) 。

输入:n = 4
输出:3
解释:F(4) = F(3) + F(2) = F(2)+F(1)+F(1)+F(0)=3F(1)+2F(0)=3

class Solution {
public:
    int fib(int n) {
        if(n<2)
            return n==1?1:0;
        int m=fib(n-1)+fib(n-2);
        return m;
    }
};

206.反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(head==NULL||head->next==NULL)
            return head;
        ListNode* newhead=reverseList(head->next);
        head->next->next=head;
        head->next=NULL;
        return newhead;
    }
};

344.反转字符串

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。

不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。

输入:s = ["H","a","n","n","a","h"]
输出:["h","a","n","n","a","H"]

//双指针,分别指向头和尾
class Solution {
public:
    void reverseString(vector<char>& s) {
        for(int i=0,j=s.size()-1;i<s.size()/2;i++,j--)
            swap(s[i],s[j]);
    }
};
//递归
class Solution {
public:
    void reverse(vector<char>&s,int left,int right){
        if(left>=right)
            return;
        reverse(s,left+1,right-1);
        swap(s[left],s[right]);
        return;
    }
    void reverseString(vector<char>& s) {
       if(s.size()==0)
           return;
       int left=0,right=s.size()-1;
       reverse(s,left,right);
    }
};

动态规划

1531.压缩字符串 II

行程长度编码 是一种常用的字符串压缩方法,它将连续的相同字符(重复 2 次或更多次)替换为字符和表示字符计数的数字(行程长度)。例如,用此方法压缩字符串 "aabccc" ,将 "aa" 替换为 "a2" ,"ccc" 替换为` "c3" 。因此压缩后的字符串变为 "a2bc3" 。

注意,本问题中,压缩时没有在单个字符后附加计数 '1' 。

给你一个字符串 s 和一个整数 k 。你需要从字符串 s 中删除最多 k 个字符,以使 s 的行程长度编码长度最小。

请你返回删除最多 k 个字符后,s 行程长度编码的最小长度 。

输入:s = "aabbaa", k = 2
输出:2
解释:如果删去两个 'b' 字符,那么压缩后的字符串是长度为 2 的 "a4" 。

给定的字符串 s 为「原串」,压缩后的字符串 t 为「压缩串」。我们的目标是从 s 中删除至多 k 个字符,使得其对应的 t 的长度最小。

压缩串 t 由字母和数字间隔组成:

cx​ 表示字母,dx​ 表示数字,(cx,dx)表示 s 中连续出现了dx​ 次字母cx​

用 f[i][j] 表示对于原串 s 的前 i 个字符,通过删除其中的 j 个字符,剩余的 i-j 个字符可以得到的最小的压缩串的长度。i 的最大值为原串 s 的长度,j 的最大值为 k 。

  • 如果第 i 个字符被删除,那么前 i-1 个字符中就有 j-1 个字符被删除,状态转移方程为:

f[i][j]=f[i−1][j−1]

  • 如果第 i 个字符没有被删除,要在 [i0, i)的范围内再选择若干个(包括零个)与 s[i] 相同的字符,一起进行压缩。在选择的范围内与 s[i] 不相同的字符,则会全部被删除。令

diff(i0,i)表示[i0,i]中与s[i]不同的字符数,same(i0,i)表示[i0,i]中与s[i]相同的字符数,有:

cost(dx​) 表示压缩 dx​ 个字符得到的长度:

 

class Solution {
public:
    int getLengthOfOptimalCompression(string s, int k) {
       int n=s.size();
       vector<vector<int>> f(n+1,vector<int>(k+1,INT_MAX>>1));
       f[0][0]=0;
       for(int i=1;i<=n;i++)
       {
           for(int j=0;j<=k&&j<=i;j++)
           {
              if(j>0)
                f[i][j]=f[i-1][j-1];
              int same=0,diff=0;
              for(int i0=i;i0>=1&&diff<=j;i0--)
              {
                  if(s[i0-1]==s[i-1])
                  {
                      same++;
         f[i][j]=min(f[i][j],f[i0-1][j-diff]+calc(same));
                  }
                  else
                    diff++;
              }
           }
       }
       return f[n][k];
    }
    int calc(int x)
    {
        if(x==1)
            return 1;
        else if(x<10)
            return 2;
        else if(x<100)
            return 3;
        else
            return 4;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值