力扣刷题之字符串
6.Z字形变换
class Solution {
public:
string convert(string s, int numRows) {
if (numRows == 1) return s;
vector<string> rows(min(numRows, int(s.size()))); // 防止s的长度小于行数
int curRow = 0;
bool goingDown = false;
for (char c : s) {
rows[curRow] += c;
if (curRow == 0 || curRow == numRows - 1) {// 当前行curRow为0或numRows -1时,箭头发生反向转折
goingDown = !goingDown;
}
curRow += goingDown ? 1 : -1;
}
string ret;
for (string row : rows) {// 从上到下遍历行
ret += row;
}
return ret;
}
};
8.字符串转换整数(atoi)
class Solution {
public:
int myAtoi(string s) {
//条件3
int r = 0;
while(s[r] == ' ' && r < s.size()) r++;
if(r == s.size()) return 0;
//条件1
int minus = 1;
if(s[r] == '-') minus = -1, r++;
else if(s[r] == '+') r++;
//条件2
long long res = 0;
while(r < s.size() && s[r] >= '0' && s[r] <= '9')
{
res = res * 10 + s[r] - '0';
r++;
if(res > INT_MAX) break;
}
//条件4
res *= minus;
if(res > INT_MAX) res = INT_MAX;
if(res < INT_MIN) res = INT_MIN;
return res;
}
};
12.整数转罗马数字
法一:模拟
const pair<int, string> valueSymbols[] = {
{1000, "M"},
{900, "CM"},
{500, "D"},
{400, "CD"},
{100, "C"},
{90, "XC"},
{50, "L"},
{40, "XL"},
{10, "X"},
{9, "IX"},
{5, "V"},
{4, "IV"},
{1, "I"},
};
class Solution {
public:
string intToRoman(int num) {
string roman;
for (const auto &[value, symbol] : valueSymbols) {
while (num >= value) {
num -= value;
roman += symbol;
}
if (num == 0) {
break;
}
}
return roman;
}
};
class Solution {
public:
string intToRoman(int num) {
int values[] = {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
string reps[] = {"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"};
string res;
for (int i = 0; i < 13; i ++ ) //这里不使用图里的count了,一遍一遍来就行了
while(num >= values[i])
{
num -= values[i];
res += reps[i];
}
return res;
}
};
13.罗马数字转整数
class Solution {
private:
unordered_map<char, int> symbolValues = {
{'I', 1},
{'V', 5},
{'X', 10},
{'L', 50},
{'C', 100},
{'D', 500},
{'M', 1000},
};
public:
int romanToInt(string s) {
int ans = 0;
int n = s.length();
for (int i = 0; i < n; ++i) {
int value = symbolValues[s[i]];
if (i < n - 1 && value < symbolValues[s[i + 1]]) {
ans -= value;
} else {
ans += value;
}
}
return ans;
}
};
14.最长公共前缀
方法一:横向扫描
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);
}
};
22.括号生成
法一:暴力法
class Solution {
bool valid(const string& str) {
int balance = 0;
for (char c : str) {
if (c == '(') {
++balance;
} else {
--balance;
}
if (balance < 0) {
return false;
}
}
return balance == 0;
}
void generate_all(string& current, int n, vector<string>& result) {
if (n == current.size()) {
if (valid(current)) {
result.push_back(current);
}
return;
}
current += '(';
generate_all(current, n, result);
current.pop_back();
current += ')';
generate_all(current, n, result);
current.pop_back();
}
public:
vector<string> generateParenthesis(int n) {
vector<string> result;
string current;
generate_all(current, n * 2, result);
return result;
}
};
法二:回溯法:
class Solution {
void backtrack(vector<string>& ans, string& cur, int open, int close, int n) {
if (cur.size() == n * 2) {
ans.push_back(cur);
return;
}
if (open < n) {
cur.push_back('(');
backtrack(ans, cur, open + 1, close, n);
cur.pop_back();
}
if (close < open) {
cur.push_back(')');
backtrack(ans, cur, open, close + 1, n);
cur.pop_back();
}
}
public:
vector<string> generateParenthesis(int n) {
vector<string> result;
string current;
backtrack(result, current, 0, 0, n);
return result;
}
};
法三:dfs+剪枝
dfs 搞出所有可能性,边d边剪枝去掉所有不符合题目要求,作用相当于回溯。但是比回溯干净利落
class Solution {
public:
vector<string> generateParenthesis(int n) {
vector<string> res;
func(res, "", 0, 0, n);
return res;
}
void func(vector<string> &res, string str, int l, int r, int n){
if(l > n || r > n || r > l) return ;
if(l == n && r == n) {res.push_back(str); return;}
func(res, str + '(', l+1, r, n);
func(res, str + ')', l, r+1, n);
return;
}
};
28.实现strStr()
法一:暴力法
class Solution {
public:
int strStr(string haystack, string needle) {
int n = haystack.size(), m = needle.size();
for (int i = 0; i + m <= n; i++) {
bool flag = true;
for (int j = 0; j < m; j++) {
if (haystack[i + j] != needle[j]) {
flag = false;
break;
}
}
if (flag) {
return i;
}
}
return -1;
}
};
法二:KMP解法
https://leetcode-cn.com/problems/implement-strstr/solution/shua-chuan-lc-shuang-bai-po-su-jie-fa-km-tb86/
KMP 算法是一个快速查找匹配串的算法,它的作用其实就是本题问题:如何快速在「原字符串」中找到「匹配字符串」。
KMP 之所以能够在 O(m + n)O(m+n) 复杂度内完成查找,是因为其能在「非完全匹配」的过程中提取到有效信息进行复用,以减少「重复匹配」的消耗。
class Solution {
public:
int strStr(string haystack, string needle) {
int n = haystack.size(), m = needle.size();
if (m == 0) {
return 0;
}
vector<int> pi(m);
for (int i = 1, j = 0; i < m; i++) {
while (j > 0 && needle[i] != needle[j]) {
j = pi[j - 1];
}
if (needle[i] == needle[j]) {
j++;
}
pi[i] = j;
}
for (int i = 0, j = 0; i < n; i++) {
while (j > 0 && haystack[i] != needle[j]) {
j = pi[j - 1];
}
if (haystack[i] == needle[j]) {
j++;
}
if (j == m) {
return i - m + 1;
}
}
return -1;
}
};
38.外观数列
class Solution {
public:
string countAndSay(int n) {
string prev = "1";
for (int i = 2; i <= n; ++i) {
string curr = "";
int start = 0;
int pos = 0;
while (pos < prev.size()) {
while (pos < prev.size() && prev[pos] == prev[start]) {
pos++;
}
curr += to_string(pos - start) + prev[start];
start = pos;
}
prev = curr;
}
return prev;
}
};
43.字符串相乘
class Solution {
public:
string multiply(string num1, string num2) {
vector<int> A, B;
int n = num1.size(), m = num2.size();
for (int i = n - 1; i >= 0; i -- ) A.push_back(num1[i] - '0');
for (int i = m - 1; i >= 0; i -- ) B.push_back(num2[i] - '0');
vector<int> C(n + m);
//模拟竖式乘法
for (int i = 0; i < n; i ++ )
for (int j = 0; j < m; j ++ )
C[i + j] += A[i] * B[j];
//考虑进位
for (int i = 0, t = 0; i < C.size(); i ++ ) {
t += C[i];
C[i] = t % 10;
t /= 10;
}
//去除前导零
int k = C.size() - 1;
while (k > 0 && !C[k]) k -- ;
string res;
while (k >= 0) res += C[k -- ] + '0';
return res;
}
};
49.字母异味词分组
法一:排序+哈希表
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<string, vector<string>> mp;
for (string& str: strs) {
string key = str;
sort(key.begin(), key.end());
mp[key].emplace_back(str);
}
vector<vector<string>> ans;
for (auto it = mp.begin(); it != mp.end(); ++it) {
ans.emplace_back(it->second);
}
return ans;
}
};
58.最后一个单词的长度
方法一:反向遍历
class Solution {
public:
int lengthOfLastWord(string s) {
int index = s.size() - 1;
while (s[index] == ' ') {
index--;
}
int wordLength = 0;
while (index >= 0 && s[index] != ' ') {
wordLength++;
index--;
}
return wordLength;
}
};
67.二进制求和
法一:模拟
class Solution {
public:
string addBinary(string a, string b) {
//倒过来算
reverse(a.begin(), a.end());
reverse(b.begin(), b.end());
int t=0;
string ans;
//t代表进位
for(int i = 0; i < a.size() || i < b.size() || t; i ++) {
if(i < a.size()) t += (a[i] - '0');
if(i < b.size()) t += (b[i] - '0');
ans += (t % 2 + '0');
t /= 2;
}
reverse(ans.begin(),ans.end());
return ans;
}
};
71.简化路径
class Solution {
public:
string simplifyPath(string path) {
stringstream is(path);
vector<string> strs;
string res = "", tmp = "";
while(getline(is, tmp, '/')) {
if(tmp == "" || tmp == ".")
continue;
else if(tmp == ".." && !strs.empty())
strs.pop_back();
else if(tmp != "..")
strs.push_back(tmp);
}
for(string str:strs)
res += "/" + str;
if(res.empty())
return "/";
return res;
}
};
91.解码方法(青蛙跳台阶)
class Solution {
public:
int numDecodings(string s) {
int n = s.size();
vector<int> f(n + 1);
f[0] = 1;
for (int i = 1; i <= n; ++i) {
if (s[i - 1] != '0') {
f[i] += f[i - 1];
}
if (i > 1 && s[i - 2] != '0' && ((s[i - 2] - '0') * 10 + (s[i - 1] - '0') <= 26)) {
f[i] += f[i - 2];
}
}
return f[n];
}
};
class Solution {
public:
int numDecodings(string s) {
int n = s.size();
// a = f[i-2], b = f[i-1], c = f[i]
int a = 0, b = 1, c;
for (int i = 1; i <= n; ++i) {
c = 0;
if (s[i - 1] != '0') {
c += b;
}
if (i > 1 && s[i - 2] != '0' && ((s[i - 2] - '0') * 10 + (s[i - 1] - '0') <= 26)) {
c += a;
}
tie(a, b) = {b, c};
}
return c;
}
};
93.复原IP地址
法一:暴力
class Solution {
public:
vector<string> restoreIpAddresses(string s) {
vector<string> res;
for (int a = 1; a < 4; a ++ )
for (int b = 1; b < 4; b ++ )
for (int c = 1; c < 4; c ++ )
for (int d = 1; d < 4; d ++ ) //abcd分别表示四段ip地址长度
{
if (a + b + c + d == s.size()) //四段长度刚好
{
string s1 = s.substr(0, a); //分别截取四段ip地址
string s2 = s.substr(a, b);
string s3 = s.substr(a + b, c);
string s4 = s.substr(a + b + c);
if (check(s1) && check(s2) && check(s3) && check(s4))
{
string ip = s1 + '.' + s2 + '.' + s3 + '.' + s4;
res.push_back(ip);
}
}
}
return res;
}
bool check(string s) //判断ip地址每段的第一位不为0,或只有一位且该位为0
{
if (stoi(s) <= 255)
if (s[0] != '0' || (s[0] == '0' && s.size() == 1)) return true;
return false;
}
};
法二:回溯
class Solution {
public:
vector<string> res;
vector<string> restoreIpAddresses(string s) {
int n = s.size();
string cur = s;
helper(n,0,-1,cur,s);
return res;
}
void helper(int n,int pointnum,int lastpoint,string& cur,string& s) {
//pointnum记录目前加了几个点了,lastpoint记录上一个点加的位置
if (pointnum == 3) {
//如果已经加了三个点了,并且最后一个点的右边表示的数小于255,则是正确IP地址
if (valid(lastpoint + 1,n-1,s)){
res.push_back(cur);
}
return;
}
//从上一个.号的下一个位置开始查找
for (int i = lastpoint + 1;i < n - 1;i++) {
//如果字符串s从上一个.号到i位置表示的数小于等于255,则符合条件
if (valid(lastpoint + 1,i,s)){
//正常回溯法,注意这里要+pointnum,因为已经加入的.号也会占位
cur.insert(cur.begin() + i + pointnum + 1,'.');
helper(n,pointnum + 1,i,cur,s);
cur.erase(pointnum + i + 1,1);
}
}
return;
}
bool valid(int left,int right,string& s) {
int sum = 0;
for (int i = left ;i <= right; i++) {
//处理0开头问题
if (left != right and s[left] == '0' ) return false;
//计算字符串s中left到right位表示的数的大小
sum = sum *10 + (s[i] - '0');
if (sum > 255) return false;
}
return true;
}
};
97.交错字符串
class Solution {
public:
bool isInterleave(string s1, string s2, string s3) {
auto f = vector < vector <int> > (s1.size() + 1, vector <int> (s2.size() + 1, false));
int n = s1.size(), m = s2.size(), t = s3.size();
if (n + m != t) {
return false;
}
f[0][0] = true;
for (int i = 0; i <= n; ++i) {
for (int j = 0; j <= m; ++j) {
int p = i + j - 1;
if (i > 0) {
f[i][j] |= (f[i - 1][j] && s1[i - 1] == s3[p]);
}
if (j > 0) {
f[i][j] |= (f[i][j - 1] && s2[j - 1] == s3[p]);
}
}
}
return f[n][m];
}
};
滚动数组 空间优化
class Solution {
public:
bool isInterleave(string s1, string s2, string s3) {
auto f = vector <int> (s2.size() + 1, false);
int n = s1.size(), m = s2.size(), t = s3.size();
if (n + m != t) {
return false;
}
f[0] = true;
for (int i = 0; i <= n; ++i) {
for (int j = 0; j <= m; ++j) {
int p = i + j - 1;
if (i > 0) {
f[j] &= (s1[i - 1] == s3[p]);
}
if (j > 0) {
f[j] |= (f[j - 1] && s2[j - 1] == s3[p]);
}
}
}
return f[m];
}
};
125.验证回文串
法一:翻转API(字符判断函数isalnum()以及转换函数tolower())
class Solution {
public:
bool isPalindrome(string s) {
string sgood;
for (char ch: s) {
if (isalnum(ch)) {
sgood += tolower(ch);
}
}
string sgood_rev(sgood.rbegin(), sgood.rend());
return sgood == sgood_rev;
}
};
法二:双指针
class Solution {
public:
bool isPalindrome(string s) {
string sgood;
for (char ch: s) {
if (isalnum(ch)) {
sgood += tolower(ch);
}
}
int n = sgood.size();
int left = 0, right = n - 1;
while (left < right) {
if (sgood[left] != sgood[right]) {
return false;
}
++left;
--right;
}
return true;
}
};
151.翻转字符串里的单词
有些语言的字符串不可变(如 Java 和 Python),有些语言的字符串可变(如 C++)。对于字符串不可变的语言,首先得把字符串转化成其他可变的数据结构,同时还需要在转化的过程中去除空格。
双端队列:
class Solution {
public:
string reverseWords(string s) {
int left = 0, right = s.size() - 1;
// 去掉字符串开头的空白字符
while (left <= right && s[left] == ' ') ++left;
// 去掉字符串末尾的空白字符
while (left <= right && s[right] == ' ') --right;
deque<string> d;
string word;
while (left <= right) {
char c = s[left];
if (word.size() && c == ' ') {
// 将单词 push 到队列的头部
d.push_front(move(word));
word = "";
}
else if (c != ' ') {
word += c;
}
++left;
}
d.push_front(move(word));
string ans;
while (!d.empty()) {
ans += d.front();
d.pop_front();
if (!d.empty()) ans += ' ';
}
return ans;
}
};
class Solution {
public:
string reverseWords(string s) {
int i = s.size() - 1;
string ans;
while(i >= 0)
{
int c = 0;
while(i >= 0 && s[i] == ' ') --i;
while(i >= 0 && s[i] != ' ')
{
--i;
++c;
}
if(c)
ans += s.substr(i+1, c) + " ";
}
return ans.substr(0, ans.size()-1);
}
};
165.比较版本号
法一:双指针
class Solution {
public:
int compareVersion(string version1, string version2) {
int n = version1.length(), m = version2.length();
int i = 0, j = 0;
while (i < n || j < m) {
int x = 0;
for (; i < n && version1[i] != '.'; ++i) {
x = x * 10 + version1[i] - '0';
}
++i; // 跳过点号
int y = 0;
for (; j < m && version2[j] != '.'; ++j) {
y = y * 10 + version2[j] - '0';
}
++j; // 跳过点号
if (x != y) {
return x > y ? 1 : -1;
}
}
return 0;
}
};
方法二:使用stringstream写字符串分割
class Solution {
public:
int compareVersion(string version1, string version2) {
stringstream ss;
vector<int> v1, v2;
string s;
ss << version1;
while (getline(ss, s, '.')) {
v1.push_back(stoi(s));
}
ss.clear();
ss << version2;
while (getline(ss, s, '.')) {
v2.push_back(stoi(s));
}
while (v1.size() < v2.size()) v1.push_back(0);
while (v2.size() < v1.size()) v2.push_back(0);
for (int i = 0; i < v1.size(); ++i) {
if (v1[i] > v2[i]) return 1;
else if (v1[i] < v2[i]) return -1;
}
return 0;
}
};
166.分数到小数
长除法
class Solution {
public:
string fractionToDecimal(int numerator, int denominator) {
long numeratorLong = numerator;
long denominatorLong = denominator;
if (numeratorLong % denominatorLong == 0) {
return to_string(numeratorLong / denominatorLong);
}
string ans;
if (numeratorLong < 0 ^ denominatorLong < 0) {
ans.push_back('-');
}
// 整数部分
numeratorLong = abs(numeratorLong);
denominatorLong = abs(denominatorLong);
long integerPart = numeratorLong / denominatorLong;
ans += to_string(integerPart);
ans.push_back('.');
// 小数部分
string fractionPart;
unordered_map<long, int> remainderIndexMap;
long remainder = numeratorLong % denominatorLong;
int index = 0;
while (remainder != 0 && !remainderIndexMap.count(remainder)) {
remainderIndexMap[remainder] = index;
remainder *= 10;
fractionPart += to_string(remainder / denominatorLong);
remainder %= denominatorLong;
index++;
}
if (remainder != 0) { // 有循环节
int insertIndex = remainderIndexMap[remainder];
fractionPart = fractionPart.substr(0,insertIndex) + '(' + fractionPart.substr(insertIndex);
fractionPart.push_back(')');
}
ans += fractionPart;
return ans;
}
};
168.Excel表列名称
class Solution {
public:
string convertToTitle(int columnNumber) {
string ans;
while (columnNumber > 0) {
--columnNumber;
ans += columnNumber % 26 + 'A';
columnNumber /= 26;
}
reverse(ans.begin(), ans.end());
return ans;
}
};
171.Excel 表列序号
class Solution {
public:
int titleToNumber(string columnTitle) {
int number = 0;
long multiple = 1;
for (int i = columnTitle.size() - 1; i >= 0; i--) {
int k = columnTitle[i] - 'A' + 1;
number += k * multiple;
multiple *= 26;
}
return number;
}
};
179.最大数
class Solution {
public:
static bool cmp(string a,string b){
string c=a+b;
string d=b+a;
//if(c>d) return true;
//else return false;
return c>d;
}
string largestNumber(vector<int>& nums) {
vector<string> num;
bool flag=false;
for(int i=0;i<nums.size();i++){
num.push_back(to_string(nums[i]));
if(nums[i]!=0) flag=true;
}
if(flag==false) return "0";
sort(num.begin(),num.end(),cmp);
string result;
for(int i=0;i<num.size();i++){
result+=num[i];
}
return result;
}
};
187.重复的DNA序列
法一:哈希表
class Solution {
const int L = 10;
public:
vector<string> findRepeatedDnaSequences(string s) {
vector<string> ans;
unordered_map<string, int> cnt;
int n = s.length();
for (int i = 0; i <= n - L; ++i) {
string sub = s.substr(i, L);
if (++cnt[sub] == 2) {
ans.push_back(sub);
}
}
return ans;
}
};
205.同构字符串
class Solution {
public:
bool isIsomorphic(string s, string t) {
unordered_map<char, char> s2t;
unordered_map<char, char> t2s;
int len = s.length();
for (int i = 0; i < len; ++i) {
char x = s[i], y = t[i];
if ((s2t.count(x) && s2t[x] != y) || (t2s.count(y) && t2s[y] != x)) {
return false;
}
s2t[x] = y;
t2s[y] = x;
}
return true;
}
};