刷题日记:面试经典 150 题 DAY4
- 125. 验证回文串
- 28. 找出字符串中第一个匹配项的下标
- 151. 反转字符串中的单词
- 6. Z 字形变换
- 68. 文本左右对齐
125. 验证回文串
原题链接 125. 验证回文串
双指针,一前一后,遇到非数字字母跳过即可
class Solution {
public:
bool isPalindrome(string s) {
int i = 0, j = s.size()-1;
while(i < j) {
while(i<j && !isalnum(s[i])) i++;
while(i<j && !isalnum(s[j])) j--;
if(tolower(s[i]) != tolower(s[j])) {
return false;
}
i++, j--;
}
return true;
}
};
28. 找出字符串中第一个匹配项的下标
原题链接 28. 找出字符串中第一个匹配项的下标
子串匹配问题,有很多算法,其中最著名的是KMP算法,时间复杂度是
O
(
M
+
N
)
O(M+N)
O(M+N)。之前短暂的理解过,现在又不明白了。贴一个讲解 可能是全网最清晰的KMP算法讲解
理解不了就硬背
class Solution {
public:
int strStr(string haystack, string needle) {
int len_n = needle.size();
int next[len_n];
next[0] = -1;
for(int i = 1, j = -1;i < len_n;i++){
while(j > -1 && needle[i] != needle[j+1]) j = next[j];
if(needle[i] == needle[j+1]) j++;
next[i] = j;
}
for(int i = 0, j = -1;i < haystack.size();i++) {
while(j > -1 && haystack[i] != needle[j+1]) j = next[j];
if(haystack[i] == needle[j+1]) j++;
if(j == len_n-1) {
return i-len_n+1;
}
}
return -1;
}
};
找到一个好理解的Sunday算法
算法仍然包括两步:预处理和移动模式串
基本流程是:
- 左对齐,开始进行匹配
- 一旦匹配失败,对“下一位”进行检查
- 找到模式串中该字母最后出现的位置,然后对齐,开启下一轮匹配
为了加速这个过程,我们预处理出一个键值对,键即字母表中的各个字母,对应的值是该字母在模式串中出现的位置(事实上,我们使用一个数组进行模拟;并且处理值,使得其意味着“此时模式串应该向后平移几位”)
class Solution {
public:
int strStr(string haystack, string needle) {
int n = haystack.size(), m = needle.size();
if(m > n) return -1;
vector<int> shifts(26,m+1);
for(int i = 0;i < m;i++) {
shifts[needle[i]-'a'] = m-i;
}
int s = 0, i;
while(s <= n-m) {
i = 0;
while(haystack[s+i] == needle[i]) {
i++;
if(i == m) {
return s;
}
}
if(s+m >= n) break;
s += shifts[haystack[s+m]-'a'];
}
return -1;
}
};
151. 反转字符串中的单词
原题链接 151. 反转字符串中的单词
- 第一步,去掉前后的空格
- 第二步,双指针,均从串末尾开始遍历,一个指向单词的开头,一个指向单词的结尾
- 交替地判断正在被遍历的是字母还是空格
class Solution {
public:
string reverseWords(string s) {
int i,j;
int len = s.size();
for(i = 0;i < len && s[i] == ' ';i++);
for(j = len-1;j >= 0 && s[j] == ' ';j--);
s = s.substr(i,j-i+1);
i = s.size()-1;
j = s.size()-1;
string result = "";
while(i >= 0) {
while(i >= 0 && s[i] != ' ') i--;
result += s.substr(i+1,j-i) + " ";
while(i >= 0 && s[i] == ' ') i--;
j = i;
}
result = result.substr(0,result.size()-1);
return result;
}
};
6. Z 字形变换
原题链接 6. Z 字形变换
直接分类讨论,设我们讨论在Z字形中的第i
行第j
个字母,其在原串中的位置是index
- 每一行的
index
都从i
开始 - 对于第一行和最后一行,
j
每增加1,index
增加2*(numRows-1)
- 对于中间
j
是偶数,则index
增加2*(numRows-i-1)
j
是奇数,则index
增加2*i
class Solution {
public:
string convert(string s, int numRows) {
int len = s.size();
if(numRows == 1 || len <= numRows) return s;
string result;
int t = 2*(numRows-1);
for(int i = 0;i < numRows;i++) {
int j = 0;
int index = i;
while(index < len) {
result.push_back(s[index]);
if(i == 0 || i == numRows-1) {
index += t;
} else if(j%2 == 0) {
index += t-2*i;
} else {
index += 2*i;
}
j++;
}
}
return result;
}
};
68. 文本左右对齐
原题链接 68. 文本左右对齐
做的我有点脑溢血了,大致分三步
- 第一步,将原单词列分成连续的区间,每个区间中的单词应该以左右对齐放在一行中
- 设单词数量是
n
n
n,单词总长度是
s
u
m
sum
sum,则满足
s
u
m
+
n
−
1
≤
m
a
x
W
i
d
t
h
sum+n-1 \leq maxWidth
sum+n−1≤maxWidth的尽可能长的一组单词应该放到一行(即,最少在每个单词之间放一个空格)
-
这一步我脑残突然发了,死活写不出来。其实简化一下问题就是:有一个数组,一个最大和maxSum。写一个函数,将数组分成几个尽可能长的连续片段,使得在每个连续片段内,数组元素的和都小于等于maxSum。代码应该是:
vector<vector<int>> splitArray(const vector<int>& nums, int maxSum) { vector<vector<int>> result; int currentSum = 0; vector<int> currentSegment; for (int num : nums) { if (currentSum + num > maxSum) { // 当前片段的和超过maxSum,将当前片段加入结果,并开始新的片段 result.push_back(currentSegment); currentSegment.clear(); currentSum = 0; } currentSegment.push_back(num); currentSum += num; } // 将最后一个片段加入结果 result.push_back(currentSegment); return result; }
- 应该是 执行循环判断是否下标越界–>再循环内先判断是否已经超出最大和–>构造序列
-
- 设单词数量是
n
n
n,单词总长度是
s
u
m
sum
sum,则满足
s
u
m
+
n
−
1
≤
m
a
x
W
i
d
t
h
sum+n-1 \leq maxWidth
sum+n−1≤maxWidth的尽可能长的一组单词应该放到一行(即,最少在每个单词之间放一个空格)
- 第二步,对每组单词,执行左右对齐。假设需要 s s s个空格放到 n n n个间隔之中去,思路是先给每个间隔分配 ⌊ s n ⌋ \lfloor \frac{s}{n} \rfloor ⌊ns⌋个空格,再给前 s m o d n s \mod n smodn个间隔多分配上一个空格
- 第三步,给剩下的单词分配左对齐。这个好说。
class Solution {
public:
struct Line {
int st;
int ed;
int lw;
};
string space_factory(int n) {
return string(n,' ');
}
vector<string> fullJustify(vector<string>& words, int maxWidth) {
int len = words.size();
int wordsize[len];
for(int i = 0;i < len;i++) {
wordsize[i] = words[i].size();
}
vector<Line> lines;
int start = 0, count = 0, sum = 0;
while(start < len) {
count = 0;
sum = 0;
while(start+count < len) {
if(sum+count+wordsize[start+count]>maxWidth) {
lines.push_back({start,start+count-1,sum});
break;
}
sum += wordsize[start+count];
count++;
}
start += count;
}
start -= count;
vector<string> res;
for(int i = 0;i < lines.size();i++) {
auto line = lines[i];
int sum_spaces = maxWidth-line.lw;
string new_line = "";
new_line += words[line.st];
if(line.st == line.ed) {
new_line += space_factory(sum_spaces);
} else {
int sigle_spaces = (sum_spaces)/(line.ed-line.st);
int remain_spaces = (sum_spaces)%(line.ed-line.st);
for(int j = 1;line.st+j<=line.ed;j++) {
if(j-1 < remain_spaces) {
new_line += space_factory(sigle_spaces+1);
} else {
new_line += space_factory(sigle_spaces);
}
new_line += words[line.st+j];
}
}
res.push_back(new_line);
}
string new_line = "";
new_line += words[start];
sum = wordsize[start];
for(int i = start+1;i < len;i++) {
new_line += " ";
new_line += words[i];
sum += wordsize[i];
}
new_line += space_factory(maxWidth-sum-(len-start-1));
res.push_back(new_line);
return res;
}
};