难度:★☆☆☆☆
类型:数组
给定一个非空的字符串,判断它是否可以由它的一个子串重复多次构成。给定的字符串只含有小写英文字母,并且长度不超过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)
", 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;
}
};