挺经典的题目,写法也很多种,最简单的做法直接O(n^2)枚举子串,再遍历子串看是否回文,整体复杂度O(n^3)。判断子串回文的过程可以用字符串哈希优化到O(1),这样整体复杂度为O(n^2)。接下来还可以用二分进一步优化,二分回文串长度就行了,二分check里面遍历一遍看是否存在这个长度的回文串,整体复杂度为O(nlogn)。最后可以用马拉车算法优化到O(n),马拉车就是为了解决这个问题专门设计出来的。
//O(n^2),枚举子串+字符串哈希
class Solution {
public:
string longestPalindrome(string s) {
s = " " + s;
vector l(s.size()+1), r(s.size()+1), p(s.size()+1);
const int P = 233;
p[0] = 1;
for(int i = 1; i < s.size(); i++)
p[i] = p[i-1]*P, l[i] = l[i-1]*P+s[i];
for(int i = s.size()-1; i >= 1; i--)
r[i] = r[i+1]*P+s[i];
int len = 0, ansl, ansr;
for(int i = 1; i < s.size(); i++)
for(int j = i; j < s.size(); j++){
if(l[j]-l[i]*p[j-i] == r[i]-r[j]*p[j-i] && j-i+1 > len){
len = j-i+1;
ansl = i, ansr = j;
}
}
return s.substr(ansl, ansr-ansl+1);
}
};
//O(nlogn),二分+字符串哈希
class Solution {
public:
vector l, r, p;
const int P = 233;
int ansl;
bool check(int x){
x = 2*x+1;
for(int i = 0; i+x-1 < l.size(); i++){
if(l[i+x-1]-l[i]*p[x-1] == r[i]-r[i+x-1]*p[x-1]){
ansl = i;
return true;
}
}
return false;
}
string longestPalindrome(string s) {
string cpy = "";
for(int i = 0; i < s.size(); i++)
cpy += s[i], cpy += '*';
s = " *" + cpy;
vector l(s.size()+1), r(s.size()+1), p(s.size()+1);
p[0] = 1;
for(int i = 1; i < s.size(); i++)
p[i] = p[i-1]*P, l[i] = l[i-1]*P+s[i];
for(int i = s.size()-1; i >= 1; i--)
r[i] = r[i+1]*P+s[i];
this->l = l, this->r = r, this->p = p;
int ans = -1, ll = 1, rr = s.size()/2-1;
while(ll <= rr){
int mid = ll+rr>>1;
if(check(mid)){
ans = mid;
ll = mid+1;
}
else rr = mid-1;
}
string res = "";
cpy = s.substr(ansl, 2*ans+1);
for(int i = 0; i < cpy.size(); i++)
if(cpy[i] != '*')
res += cpy[i];
return res;
}
};
也是挺经典的题,双指针就行了,过程中维护一个map,标记区间内出现过的字符。
class Solution {
public:
int lengthOfLongestSubstring(string s) {
vector mp(512);
int ans = 0;
for(int l = 0, r = 0; l < s.size(); l++){
while(r < s.size() && mp[s[r]] == 0){
mp[s[r]] = 1;
r++;
}
ans = max(ans, r-l);
mp[s[l]] = 0;
}
return ans;
}
};
和上面一道题类似,也是用双指针解决,只不过过程中需要维护的东西多了点。首先需要统计出来t串中出现了几种不同的字符以及各自的出现次数,然后双指针过程中维护s串区间内出现了多少种满足要求(即区间内该字符出现次数大于等于t串内的出现次数)的不同字符以及各自出现次数,其余部分就和上一题一样了。
class Solution {
public:
string minWindow(string s, string t) {
int nums = 0, numt = 0;
vector mps(512), mpt(512);
for(int i = 0; i < t.size(); i++){
if(mpt[t[i]] == 0) numt++;
mpt[t[i]]++;
}
int ans = 0x3f3f3f3f, ansl, ansr;
for(int l = 0, r = 0; l < s.size(); l++){
while(r < s.size() && nums < numt){
if(mps[s[r]]+1 == mpt[s[r]]) nums++;
mps[s[r]]++;
r++;
}
if(nums == numt && ans > r-l){
ans = r-l;
ansl = l, ansr = r-1;
}
if(mps[s[l]] == mpt[s[l]]) nums--;
mps[s[l]]--;
}
if(ans != 0x3f3f3f3f) return s.substr(ansl, ansr-ansl+1);
return "";
}
};
开一个vector
//普通写法
class Solution {
public:
string reverseWords(string s) {
vector a;
string word = "";
for(int i = 0; i < s.size(); i++){
if(s[i] != ' ')
word += s[i];
else{
if(word != "") a.push_back(word);
word = "";
}
}
if(word != "") a.push_back(word);
reverse(a.begin(), a.end());
string res = a[0];
for(int i = 1; i < a.size(); i++)
res += " ", res += a[i];
return res;
}
};
//进阶要求的空间优化写法
class Solution {
public:
string reverseWords(string s) {
int len = 0;
for(int i = 0; i < s.size(); i++){
if(s[i] != ' ') s[len++] = s[i];
else if(len > 0 && s[len-1] != ' ') s[len++] = ' ';
}
if(s[len-1] == ' ') len--;
s = s.substr(0, len);
reverse(s.begin(), s.end());
int l = -1, r;
for(int i = 0; i < s.size(); i++){
if(s[i] != ' '){
if(l == -1) l = i;
r = i;
}
else{
reverse(s.begin()+l, s.begin()+r+1);
l = -1;
}
}
reverse(s.begin()+l, s.begin()+r+1);
return s;
}
};
如果中括号不能嵌套那就很简单了,但这题括号可以嵌套,这种涉及到优先级的情况一般要使用栈来辅助。可以开两个栈,第一个栈存储字符串,第二个栈存储重复次数,然后遍历原字符串,遇到数字直接丢进数字栈,遇到字符串直接丢进字符串栈,然后遇到左括号也需要丢进字符串栈,如果遇到右括号就要开始运算了,字符串栈不断弹栈直到弹出左括号,这些字符串可以拼接为一个长字符串,然后这个长字符串重复次数就是数字栈的栈顶,若干次拼接后得到一个更长的字符串,接下来把该串再次入字符串栈。遍历完原串后数字栈一定为空,字符串栈中可能有若干元素,把字符串栈中所有字符串拼接起来就是答案。
class Solution {
public:
string decodeString(string s) {
string res = "", t = "";
stack st1;
stack st2;
int num = 0;
for(int i = 0; i < s.size(); i++){
if(s[i] >= '0' && s[i] <= '9') num = num*10+s[i]-'0';
else if(s[i] >= 'a' && s[i] <= 'z') t += s[i];
else if(s[i] == ']'){
if(t != ""){
st1.push(t);
t = "";
}
string a = st1.top(), sum = "";
st1.pop();
while(a != "["){
sum = a+sum;
a = st1.top();
st1.pop();
}
string all = "";
for(int j = 1; j <= st2.top(); j++)
all += sum;
st2.pop();
st1.push(all);
}
else{
if(num){
st2.push(num);
num = 0;
}
if(t != ""){
st1.push(t);
t = "";
}
st1.push("[");
}
}
if(t != "") st1.push(t);
while(st1.size()){
res = st1.top()+res;
st1.pop();
}
return res;
}
};
两重循环遍历一下就行了,外层遍历第i个字符,内层确定字符串数组中所有字符串第i个字符是否都相同,如果都相同就可以加入结果字符串。这题字符串哈希+二分看似能优化,其实会更慢,因为预处理哈希的过程就和暴力复杂度一样了。
这道题还有个更简单的方法,直接对字符串数组排序,然后看首尾两字符串最长公共前缀。
class Solution {
public:
string longestCommonPrefix(vector& strs) {
int mn = 0x3f3f3f3f;
for(int i = 0; i < strs.size(); i++)
mn = min(mn, (int)strs[i].size());
string res = "";
for(int i = 0; i < mn; i++){
bool flag = false;
for(int j = 0; j < strs.size()-1; j++){
if(strs[j][i] != strs[j+1][i]){
flag = true;
break;
}
}
if(!flag) res += strs[0][i];
else break;
}
return res;
}
};