字符串题目汇总
难度:★☆☆☆☆
类型:数组
给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过10000。
示例
示例 1:
输入: "abab"
输出: True
解释: 可由子字符串 "ab" 重复两次构成。
示例 2:
输入: "aba"
输出: False
示例 3:
输入: "abcabcabcabc"
输出: True
解释: 可由子字符串 "abc" 重复四次构成。 (或者子字符串 "abcabc" 重复两次构成。)
class Solution { public: bool repeatedSubstringPattern(string s) { // 常规解法,暴力思路,不同长度,依次进行比较判断 // int len = s.length(); // int m = len/2; // for(int i=1;i<=m;i++){ // int flag =1; // if(len%i==0){ // string tmp= s.substr(0,i); // int n= len/i; // for(int k= 1;k<n;k++){ // if(tmp!=s.substr(k*i,i)){ // flag =0; // break; // } // } // if (flag==1){ // return true; // } // } // } // return false; //https://leetcode-cn.com/problems/repeated-substring-pattern/solution/tu-jie-yi-xia-shuang-bei-zi-fu-chuan-de-jie-fa-by-/ //双倍字符串的思路:图解一下双倍字符串的解法 - 重复的子字符串 - 力扣(LeetCode return (s+s).find(s,1)!=s.size(); } };
请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度。
示例 1:
输入: "abcabcbb"
输出: 3
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。
示例 2:
输入: "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。
示例 3:
输入: "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。
class Solution { public: int lengthOfLongestSubstring(string s) { // int len = s.length(); // if(len<=1){ // return len; // } // vector<int> res(len,1); // res[0]=1; // int start = 0; // int max = 1; // for(int i=1; i<len; i++){ // string tmp = s.substr(start,i-start); // if(tmp.find(s[i])!=string::npos){ // res[i]=res[i-1]; // start = tmp.find(s[i])+1; // } // else{ // res[i]=i-start+1; // } // if(res[i]>max){ // max = res[i]; // } // } // return max; int len = s.length(); int max = 1; if(len<=1){ return len; } vector<int> res(len,1); int flag = 0; int start = 0; int i=0; int j=0; for (i=1;i<len; i++){ for(j=i-1;j>=start;j--){ if(s[i]==s[j]){ flag = 1; break; } } if(!flag){ res[i] = res[i-1]+1; } else{ res[i]=i-j; start = j; flag = 0; } if(max < res[i]){ max=res[i]; } } return max; } };
动态规划思路:https://blog.csdn.net/qq_27690765/article/details/105439787
dp[i] 表示以第 i 个字符结尾的最长子字符串的长度。
分两种情况:
1)如果str[i] 和前面的所有字符都不一样,则dp[i]=dp[i-1] +1
2)如果str[i] 和前面的某个字符都一样,则以str[i] 结尾的最大不重复的字串 dp[i]=i-j (0<j<i)
滑动窗口思路,解题框架见下面字符串题目:
class Solution { public: int lengthOfLongestSubstring(string s) { unordered_map<char, int> window; int left = 0, right = 0; int res = 0; // 记录结果 while (right < s.size()) { char c = s[right]; right++; // 进行窗口内数据的一系列更新 window[c]++; // 判断左侧窗口是否要收缩 while (window[c] > 1) { char d = s[left]; left++; window[d]--; } // 在这里更新答案 res = max(res, right - left); } return res; } };
在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。
示例:
s = "abaccdeff"
返回 "b"
s = ""
返回 " "
限制:
0 <= s 的长度 <= 50000
class Solution { public: char firstUniqChar(string s) { int len = s.length(); if(len==0){ return ' '; } int hash_char[256] = {0}; for(int i=0;i<len;i++){ hash_char[s[i]]++; } for(int j=0;j<len;j++){ if(hash_char[s[j]]==1){ return s[j]; } } return ' '; } };
写一个函数 StrToInt,实现把字符串转换成整数这个功能。不能使用 atoi 或者其他类似的库函数。
首先,该函数会根据需要丢弃无用的开头空格字符,直到寻找到第一个非空格的字符为止。
当我们寻找到的第一个非空字符为正或者负号时,则将该符号与之后面尽可能多的连续数字组合起来,作为该整数的正负号;假如第一个非空字符是数字,则直接将其与之后连续的数字字符组合起来,形成整数。
该字符串除了有效的整数部分之后也可能会存在多余的字符,这些字符可以被忽略,它们对于函数不应该造成影响。
注意:假如该字符串中的第一个非空格字符不是一个有效整数字符、字符串为空或字符串仅包含空白字符时,则你的函数不需要进行转换。
在任何情况下,若函数不能进行有效的转换时,请返回 0。
说明:
假设我们的环境只能存储 32 位大小的有符号整数,那么其数值范围为 [−231, 231 − 1]。如果数值超过这个范围,请返回 INT_MAX (231 − 1) 或 INT_MIN (−231) 。
示例 1:
输入: "42"
输出: 42
示例 2:
输入: " -42"
输出: -42
解释: 第一个非空白字符为 '-', 它是一个负号。
我们尽可能将负号与后面所有连续出现的数字组合起来,最后得到 -42 。
示例 3:
输入: "4193 with words"
输出: 4193
解释: 转换截止于数字 '3' ,因为它的下一个字符不为数字。
示例 4:
输入: "words and 987"
输出: 0
解释: 第一个非空字符是 'w', 但它不是数字或正、负号。
因此无法执行有效的转换。
示例 5:
输入: "-91283472332"
输出: -2147483648
解释: 数字 "-91283472332" 超过 32 位有符号整数范围。
因此返回 INT_MIN (−231)
class Solution { public: int strToInt(string str) { int len = str.length(); if(len==0){ return 0; } int i=0; //跳过前面空格,判断正负 while(str[i]==' '){ i++; } bool neg = false; //对非空和非+-号的有效数字进行处理 if(str[i]=='-'){ neg=true; i++; }else if(str[i]=='+'){ i++; } long ans =0; for(;i<len;i++){ if(str[i]>='0'&&str[i]<='9'){ //***字符相减转换成int ans = ans*10+(str[i]-'0'); if(ans > INT_MAX && neg) return INT_MIN; if(ans > INT_MAX && !neg) return INT_MAX; } //碰到非有效数字就退出 else{ break; } } return neg?-ans:ans; } };
- 最长回文子串:找到给出字符串可能构成的最长回文长度
思路:中心扩展法 、动态规划法
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
示例 2:
输入: "cbbd" 输出: "bb"
解法一:动态规划
从回文子串的定义我们可以得出两个结论:
(1)如果说一个子字符串是回文子串,那么给这个子字符串两边分别加一个数,如果这2个数相等,那么这个新的子字符串也是回文子串。
(2)回文子串的开始和结束位置的字符一定是相等的。
我们可以用i来表示一个子字符串的起始位置,j来表示一个子字符串的结束位置,用一个二维数组来存储i和j之间的子字符串是否是回文子串。
状态转移方程:
dp[i][j] :
i=j时为true ;
i-j=1时若s[i]==s[j]则为true
i-j>1时若s[i]==s[j]&&dp[i-1][j-1]都为真,则为true;
class Solution { public: string longestPalindrome(string s) { //动态规划做 int l=s.size(); if(l==0) return ""; int max_l=0,max_r=0; vector<vector<int>>dp(l,vector<int>(l,0)); for(int i=0;i<l;++i) dp[i][i]=1; for(int right=1;right<l;++right) { for(int left=0;left<right;++left) { if(s[left]==s[right]&&(right-left==1||dp[left+1][right-1])) { dp[left][right]=1; if(right-left>max_r-max_l) { max_r=right; max_l=left; } } } } return s.substr(max_l,max_r-max_l+1); } };
解法2:中心扩展法,以每一个字符或者两个字符分别作为回文串中心,向两端扩展,判断是否依旧是回文串
class Solution { public: string longestPalindrome(string s) { int n = s.size(), rmax = 0; string res; // 中心扩展法(对称) for(int k = 0; k < n; k ++) { int i = k - 1, j = k + 1; while(i >= 0 && j < n && s[i] == s[j]) i --, j ++; int len = j - i - 1; if (len > rmax) { rmax = len; res = s.substr(i + 1, len); } } for (int k = 0; k < n - 1; k ++) { int i = k, j = k + 1; while(i >= 0 && j < n && s[i] == s[j]) i --, j ++; int len = j - i - 1; if (len > rmax) { rmax = len; res = s.substr(i + 1, len); } } return res; } };
回文子串
给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串。
示例 1:
输入:"abc"
输出:3
解释:三个回文子串: "a", "b", "c"
示例 2:
输入:"aaa"
输出:6
解释:6个回文子串: "a", "a", "a", "aa", "aa", "aaa"
提示:
输入的字符串长度不会超过 1000 。
中心扩展法:
class Solution { public: int countSubstrings(string s) { int ans = 0; int len = s.length(); for(int i=0;i<2*len-1;i++){ int left = i/2; int right = left + i%2; while(left>=0&&right<len&&s[left]==s[right]){ ans++; left--; right++; } } return ans; } };
字符串有三种编辑操作:插入一个字符、删除一个字符或者替换一个字符。 给定两个字符串,编写一个函数判定它们是否只需要一次(或者零次)编辑。
示例 1:
输入:
first = "pale"
second = "ple"
输出: True
示例 2:
输入:
first = "pales"
second = "pal"
输出: False
思路1,采用编辑距离:编辑距离大于1则返回false,否则返回true;
思路2,双指针的思路
特殊情况判断,长度差大于1的直接返回false
然后遍历比较两个字符串,first[i] == second[j]) 则继续
如果:first[i] == second[j+1] ,则 j++编辑次数 op_cnt++
first[i+1] == second[j,则i++编辑次数 op_cnt++
否则:i++,j++ op_cnt++
如果,op_cnt>1 返回false
最终遍历完成,如果剩下的字符个数和op_cnt相加大于1也返回false
class Solution { public: bool oneEditAway(string first, string second) { int len1 = first.size(), len2 = second.size(); if (abs(len1 - len2) > 1) return false; int i = 0, j = 0; int op_cnt = 0; while (i < len1 && j < len2) { if (first[i] == second[j]) { i++, j++; } else { if (first[i] == second[j+1]) { j++; if (op_cnt > 0) return false; else op_cnt++; } else if (first[i+1] == second[j]) { i++; if (op_cnt > 0) return false; else op_cnt++; } else { i++, j++; if (op_cnt > 0) return false; else op_cnt++; } } } if (max(len1 - i, len2 - j) + op_cnt > 1) return false; return true; } };
有效的括号
给定一个只包括 '(',')','{','}','[',']' 的字符串,判断字符串是否有效。
有效字符串需满足:
左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
示例 1:
输入: "()"
输出: true
示例 2:
输入: "()[]{}"
输出: true
示例 3:
输入: "(]"
输出: false
示例 4:
输入: "([)]"
输出: false
示例 5:
输入: "{[]}"
输出: true
思路:用栈来实现,遇到左括号入栈,右括号出栈;
class Solution { public: bool isValid(string s) { int len = s.size(); if(len%2!=0){ return false; } stack<char> st; st.push(s[0]); for(int i=1;i<len;i++){ if(s[i]=='('||s[i]=='{'||s[i]=='['){ st.push(s[i]); }else if(!st.empty()){ if(s[i]==']'){ if(st.top()=='['){ st.pop(); }else{ return false; } } if(s[i]==')'){ if(st.top()=='('){ st.pop(); }else{ return false; } } if(s[i]=='}'){ if(st.top()=='{'){ st.pop(); }else{ return false; } } } else if(st.empty()){ return false; } } if(st.empty()){ return true; } return false; } };
最小覆盖子串:
给你一个字符串 S、一个字符串 T 。请你设计一种算法,可以在 O(n) 的时间复杂度内
,从字符串 S 里面找出:包含 T 所有字符的最小子串。
示例:
输入:S = "ADOBECODEBANC", T = "ABC"
输出:"BANC"
提示:
如果 S 中不存这样的子串,则返回空字符串 ""。
如果 S 中存在这样的子串,我们保证它是唯一的答案。
暴力思路:
for (int i = 0; i < s.size(); i++) for (int j = i + 1; j < s.size(); j++) if s[i:j] 包含 t 的所有字母: 更新答案
解题思路:滑动窗口
/* 滑动窗口算法框架 */ void slidingWindow(string s, string t) { unordered_map<char, int> need, window; for (char c : t) need[c]++; int left = 0, right = 0; int valid = 0; while (right < s.size()) { // c 是将移入窗口的字符 char c = s[right]; // 右移窗口 right++; // 进行窗口内数据的一系列更新 ... /*** debug 输出的位置 ***/ printf("window: [%d, %d)\n", left, right); /********************/ // 判断左侧窗口是否要收缩 while (window needs shrink) { // d 是将移出窗口的字符 char d = s[left]; // 左移窗口 left++; // 进行窗口内数据的一系列更新 ... } } }
class Solution { public: string minWindow(string s, string t) { unordered_map<char, int> need, window; for (char c : t) need[c]++; int left = 0, right = 0; int valid = 0; // 记录最小覆盖子串的起始索引及⻓度 int start = 0, len = INT_MAX; while (right < s.size()) { // c 是将移入窗口的字符 char c = s[right]; // 右移窗口 right++; // 进行窗口内数据的一系列更新 if (need.count(c)) { window[c]++; if (window[c] == need[c]) //满足的字符的数量 valid++; } // 判断左侧窗口是否要收缩 while (valid == need.size()) { // 在这里更新最小覆盖子串 if (right - left < len) { start = left; len = right - left; } // d 是将移出窗口的字符 char d = s[left]; // 左移窗口 left++; // 进行窗口内数据的一系列更新 if (need.count(d)) { if (window[d] == need[d]) valid--; window[d]--; } } } return len == INT_MAX ? "" : s.substr(start, len); } };
给定两个字符串 s1 和 s2,写一个函数来判断 s2 是否包含 s1 的排列。
换句话说,第一个字符串的排列之一是第二个字符串的子串。
示例1:
输入: s1 = "ab" s2 = "eidbaooo" 输出: True 解释: s2 包含 s1 的排列之一 ("ba").
示例2:
输入: s1= "ab" s2 = "eidboaoo" 输出: False
注意:
- 输入的字符串只包含小写字母
- 两个字符串的长度都在 [1, 10,000] 之间
思路:同样滑动窗口
注意,输入的 s1 是可以包含重复字符的,所以这个题难度不小。
这种题目,是明显的滑动窗口算法,相当给你一个 S 和一个 T ,请问你 S 中是否存在一个子串,
包含 T 中所有字符且不包含其他字符?
class Solution { public: bool checkInclusion(string t, string s) { unordered_map<char, int> need, window; for (char c : t) need[c]++; int left = 0, right = 0; int valid = 0; while (right < s.size()) { char c = s[right]; right++; // 进行窗口内数据的一系列更新 if (need.count(c)) { window[c]++; if (window[c] == need[c]) valid++; } // 判断左侧窗口是否要收缩,这里判断条件是是否大于目标串的长度 while (right - left >= t.size()) { // 在这里判断是否找到了合法的子串 if (valid == need.size()) return true; char d = s[left]; left++; // 进行窗口内数据的一系列更新 if (need.count(d)) { if (window[d] == need[d]) valid--; window[d]--; } } } // 未找到符合条件的子串 return false; } };
对于这道题的解法代码,基本上和最小覆盖子串一模一样,只需要改变两个 地方:
1、本题移动 left 缩小窗口的时机是窗口大小大于 t.size() 时,应为排列嘛,显然⻓度应该是一样的。
2、当发现 valid == need.size() 时,就说明窗口中就是一个合法的排列, 所以立即返回 true 。
至于如何处理窗口的扩大和缩小,和最小覆盖子串完全相同。
给定一个字符串 s 和一个非空字符串 p,找到 s 中所有是 p 的字母异位词的子串,返回这些子串的起始索引。
字符串只包含小写英文字母,并且字符串 s 和 p 的长度都不超过 20100。
说明:
- 字母异位词指字母相同,但排列不同的字符串。
- 不考虑答案输出的顺序。
示例 1:
输入: s: "cbaebabacd" p: "abc" 输出: [0, 6] 解释: 起始索引等于 0 的子串是 "cba", 它是 "abc" 的字母异位词。 起始索引等于 6 的子串是 "bac", 它是 "abc" 的字母异位词。
示例 2:
输入: s: "abab" p: "ab" 输出: [0, 1, 2] 解释: 起始索引等于 0 的子串是 "ab", 它是 "ab" 的字母异位词。 起始索引等于 1 的子串是 "ba", 它是 "ab" 的字母异位词。 起始索引等于 2 的子串是 "ab", 它是 "ab" 的字母异位词。
思路:跟寻找字符串的排列一样,只是找到一个合法异位词(排列)之后将起始索 引加入 即可。
class Solution { public: vector<int> findAnagrams(string s, string p) { unordered_map<char, int> need, window; vector<int> res; for (char c : p) need[c]++; int left = 0, right = 0; int valid = 0; while (right < s.size()) { char c = s[right]; right++; // 进行窗口内数据的一系列更新 if (need.count(c)) { window[c]++; if (window[c] == need[c]) valid++; } // 判断左侧窗口是否要收缩,这里判断条件是是否大于目标串的长度 while (right - left >= p.size()) { // 在这里判断是否找到了合法的子串 if (valid == need.size()) res.push_back(left); char d = s[left]; left++; // 进行窗口内数据的一系列更新 if (need.count(d)) { if (window[d] == need[d]) valid--; window[d]--; } } } // 未找到符合条件的子串 return res; } };
输入两个值n和k,n表示我们有从1到n个整数,然后将这些整数都字符串化之后按字典排序,找出其中第K大的。
例如:n=15,k=5.那么1-15字符串化之后排序如下:1,10,11,12,13,14,15,2,3,4,5,6,7,8,9。其中第5大的就为13。
求解: 限定n<100,求解一百以内的排序。
思路:
1,10,11,12,13,14,15,16,17,18,19,2,20,21,22,23,…
将数列分层,即1–>19为一层,一共为11个元素,记作:l=(x-10)/10; 对于一层内,存在如下关系(不包括元素0):(l+1)*10为高位,余数为低位。
#include <iostream> using namespace std; int main(){ int n=37,k; int l; for(k=1;k<=n;k++){ // cout<<" k "<<k; if(k==1){ cout<<1<<" "; continue; } if(n<10){ cout<<k<<endl; } else{ l=(n-10)/10; if(k<=11*l){ int a=(k-1)/11; int b=k-a*11; if(b==1){ // cout<<" a "<<a+1<<endl; cout<<a+1<<" "; continue; } // cout<<" b "<<(a+1)*10+b-2<<endl; cout<<(a+1)*10+b-2<<" "; } else{ int b=n-10-10*l; int a1=k/11; int b1=k-l*11; if(b1==1){ cout<<a1+1<<" "; // cout<<" e "<<a1+1<<endl; continue; } if(b1<=b+2){ cout<<(a1+1)*10+b1-2<<" "; // cout<<" c "<<(a1+1)*10+b1-2<<endl; } else{ // cout<<" d "<<b1-b-1+l<<endl; cout<<b1-b-1+l<<" "; } } } } }
// 另一种写法 #include <iostream> #include <cstdlib> using namespace std; int main(){ int n,k; cin>>n>>k; if(k == 1) cout<<1<<endl; else{ k--; int base = 10; int res; while(k > 0){ res = base; while(res<n && k>0){ res*=10; k--; } res=res/10; while(res<n && k>0){ res++; k--; } base++; } cout<<res<<endl; } return 0; }
387. 字符串中的第一个唯一字符
给定一个字符串,找到它的第一个不重复的字符,并返回它的索引。如果不存在,则返回 -1。
示例:
s = "leetcode"
返回 0
s = "loveleetcode"
返回 2
提示:你可以假定该字符串只包含小写字母。
思路:hash表的思想,利用ASCII码表,定义一个长度为256的数组,遍历字符串,统计字符出现的次数;
最后重新遍历字符串,找到一个计数为1的字符即可;
#include <iostream> //利用ASCII码表; using namespace std; class Solution { public: int firstUniqChar(string s) { int p[256] = { 0 }; int n = s.size(); for (int i = 0; i < n; i++){ p[s[i]] += 1; } for (int j = 0; j < n; j++){ if (p[s[j]] == 1){ return j; } } return -1; } }; int main() { Solution temp; string s = "loveleetcode"; cout << temp.firstUniqChar(s) << endl; system("pause"); return 0; }
14. 最长公共前缀
编写一个函数来查找字符串数组中的最长公共前缀。
如果不存在公共前缀,返回空字符串 ""。
示例 1:
输入: ["flower","flow","flight"]
输出: "fl"
示例 2:
输入: ["dog","racecar","car"]
输出: ""
解释: 输入不存在公共前缀。
思路:依次遍历字符串数组中的每个字符串,对于每个遍历到的字符串,更新最长公共前缀,当遍历完所有的字符串以后,即可得到字符串数组中的最长公共前缀。
class Solution { public: string longestCommonPrefix(vector<string>& strs) { if (!strs.size()) { return ""; } string prefix = strs[0]; int count = strs.size(); // 遍历整个数组,得到所有数组的结果 for (int i = 1; i < count; ++i) { prefix = longestCommonPrefix(prefix, strs[i]); if (!prefix.size()) { break; } } return prefix; } //先求两个字符串的最长公共前缀 string longestCommonPrefix(const string& str1, const string& str2) { int length = min(str1.size(), str2.size()); int index = 0; while (index < length && str1[index] == str2[index]) { ++index; } return str1.substr(0, index); } };
6. Z 字形变换
将一个给定字符串根据给定的行数,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 "LEETCODEISHIRING" 行数为 3 时,排列如下:
L C I R
E T O E S I I G
E D H N
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"LCIRETOESIIGEDHN"。
思路:找到字符串的排列规律,具体规律参见:https://zhuanlan.zhihu.com/p/103001655
class Solution { public: string convert(string s, int numRows) { if (numRows == 1) return s;//只有一行,直接返回 int l=s.size(); if(l<=numRows)//如果长度小于行数,也是直接返回的 return s; int step = numRows * 2 - 2; // 间距 int index ;// 记录s的下标,表明下一个需要放入的元素。 int juli = 0; // 这是每一步的步长。 string res; for (int i = 0; i < numRows; i++) // i表示行号 { index = i; juli = i * 2; while (index < l)//超出字符串长度计算下一层 { res += s[index]; // 当前行的第一个字母 juli = step - juli;// 第一次步长是step -2*i,第二次是2*i,相当于a=b-c,c=b-a,这样不断循环。 index += (i == 0 || i == numRows-1) ? step : juli; // 0行和最后一行使用step间距,其余使用add间距 } } return res; } };
763. 划分字母区间
字符串 S 由小写字母组成。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。返回一个表示每个字符串片段的长度的列表。
示例:
输入:S = "ababcbacadefegdehijhklij"
输出:[9,7,8]
解释:
划分结果为 "ababcbaca", "defegde", "hijhklij"。
每个字母最多出现在一个片段中。
像 "ababcbacadefegde", "hijhklij" 的划分是错误的,因为划分的片段数较少。
思路:
用vector容器ends来存放26个字母中各个字母在字符串中出现的最后位置。
j从0遍历至字符串结束,先确定pos为j所在字符的最后一个位置,即ends[S[j]-'a'],
在j+1到pos之间,用k遍历,判断每个字符字母的最后位置,比较是否大于pos,以此来更新pos,直到k==pos,说明一个小片段已经形成,加入答案中,令j=pos+1继续。
参考:https://blog.csdn.net/Yirschen/article/details/105353518
class Solution { public: vector<int> partitionLabels(string S) { vector<int> res; //ends存放字符串中各个字符(共26个,每一个按下标0--26表示)的最后位置 vector<int> ends(26,-1); //记录每个字符最后出现的位置 for(int i=0;i<S.size();i++){ ends[S[i]-'a']=i; } int j=0; while(j<S.size()){ //pos为第j个字符的字母在字符串中的最后位置 int pos=ends[S[j]-'a']; //判断在第j+1~pos个字符串中间是否存在某个字符在pos后面,若有,更新pos for(int k=j+1;k<=pos;++k){ pos=max(pos,ends[S[k]-'a']); } //到这个位置,说明一个小片段已经形成,加入答案列表中,j更新为pos+1 res.push_back(pos-j+1); j=pos+1; } return res; } };
#include <iostream> #include <algorithm> #include <vector> #include <string> using namespace std; vector<int> partitionLabels(string S) { vector<int> ans,ends(26, -1);//ends存放字符串中各个字符(共26个,每一个按下标0--26表示)的最后位置 for (int i = 0; i < S.length(); ++i) {//更新每个字母的最后位置 ends[S[i] - 'a'] = i; } int i = 0; while (i < S.size()) { int r = ends[S[i] - 'a'];//r为第i个字符的字母在字符串中的最后位置 //判断在第i+1--r个字符串中间是否存在某个字符在r后面,若有,更新r for (int j = i + 1; j <= r; ++j) { r = max(r, ends[S[j] - 'a']); } //到这个位置,说明一个小片段已经形成,加入答案列表中,i更新为r+1 ans.push_back(r - i + 1); i = r + 1; } return ans; } int main() { string s; cin >> s; vector<int> a = partitionLabels(s); for (vector<int>::iterator it = a.begin(); it != a.end(); ++it) { cout << *it << " "; } return 0; }
1143. 最长公共子序列
给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列的长度。
一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。两个字符串的「公共子序列」是这两个字符串所共同拥有的子序列。
若这两个字符串没有公共子序列,则返回 0。
示例 1:
输入:text1 = "abcde", text2 = "ace"
输出:3
解释:最长公共子序列是 "ace",它的长度为 3。
示例 2:
输入:text1 = "abc", text2 = "abc"
输出:3
解释:最长公共子序列是 "abc",它的长度为 3。
示例 3:
输入:text1 = "abc", text2 = "def"
输出:0
解释:两个字符串没有公共子序列,返回 0。
思路:动态规划思路,字符串str1前i个字符和str2前j个字符的最长公共子串长度,dp[i][j]
str1[i]==str2[j]时,dp[i][j]=dp[i-1][j-1]+1;
str1[i]!=str2[j]时,dp[i][j]=max(dp[i][j-1],dp[i-1][j]);
当i=0或者j=0时,dp[i][j]=0;
class Solution { public: int longestCommonSubsequence(string text1, string text2) { int len1=text1.size(); int len2=text2.size(); if(len1==0||len2==0){ return 0; } vector<vector<int>> dp(len1+1,vector<int>(len2+1)); for(int i=1;i<len1+1;i++){ for(int j=1;j<len2+1;j++){ if(text1[i-1]==text2[j-1]){ dp[i][j]=dp[i-1][j-1]+1; }else{ dp[i][j]=max(dp[i][j-1],dp[i-1][j]); } } } return dp[len1][len2]; } };
Leetcode 394. 字符串编码
给定一个经过编码的字符串,返回它解码后的字符串。
编码规则为: k[encoded_string],表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。
你可以认为输入字符串总是有效的;输入字符串中没有额外的空格,且输入的方括号总是符合格式要求的。
此外,你可以认为原始数据不包含数字,所有的数字只表示重复的次数 k ,例如不会出现像 3a 或 2[4] 的输入。
示例:
s = "3[a]2[bc]", 返回 "aaabcbc".
s = "3[a2[c]]", 返回 "accaccacc".
s = "2[abc]3[cd]ef", 返回 "abcabccdcdcdef".
求解
本题中明显有括号的匹配问题,因此需要使用栈来求解。当碰到右括号(])时,字符串出栈,碰到左括号([)时,
保存左右括号内的字符串([]),继续出栈,保存字符串重复次数,直至栈为空或碰到非数字。要注意重复次数不是个位数,
将字符串重复之后压入栈中。继续处理剩余字符串,同样执行上述过程,直至处理完字符串。然后将栈中所有的字符出栈构成结果字符串返回。
设计两个栈,一个用来保存字符串,一个用来保存出现次数;
1)如果遇到的是数字,直到遇到左括号前,求出整数,然后入栈数字,入栈当前字符串,然后置为空
2)当遇到右括号时,然后看数字栈中有多少重复次数,出栈栈顶字符串,重复对应次数
3)其它字符不处理,直接拼接上去
思考:其实写代码的时候,可以多思考,尝试一下,把想到的表达出来,或者写出来
class Solution { public: string decodeString(string s) { string cur =""; stack<string> st; stack<int> num; int sum; for(int i=0;i<s.size();i++){ if(s[i]>='0'&&s[i]<='9'){ sum=0; while(s[i]!='['){ sum = sum*10 + s[i++] - '0'; } num.push(sum); st.push(cur); cur = ""; } else if(s[i]==']'){ string temp=st.top(); for(int j=0;j<num.top();j++){ temp = temp + cur; } cur = temp; st.pop(); num.pop(); } else{ cur = cur+s[i]; } } return cur; } };
class Solution { public: string decodeString(string s) { stack<int> numStack; stack<string> strStack; string cur = ""; string result = ""; //保存展开后的字符串 int num = 0; for(int i=0;i<s.length();i++) { if(s[i]>='0'&&s[i]<='9') { num = 10*num + s[i] - '0'; } else if(s[i] == '[') { numStack.push(num); strStack.push(cur); num=0; cur.clear(); } else if(s[i]>='a' && s[i]<='z' || s[i]>='A' && s[i]<='Z') { cur += s[i]; } else if(s[i] == ']') { int k = numStack.top(); numStack.pop(); for(int j=0;j<k;j++) //重复的过程 { strStack.top()+=cur; } cur = strStack.top(); strStack.pop(); } } result += cur; return result; } };