文章目录
数据结构-字符串
- 回文子字符串个数 647. Palindromic Substrings(Medium)
- 统计二进制字符串中连续 1 和连续 0 数量相同的子字符串个数 696. Count Binary Substrings (Easy)
- 子序列计数 115. Distinct Subsequences(Hard)
- 最短的回文字符串 214. Shortest Palindrome(Hard)
字符串翻转
字符串循环移位包含、字符串循环移位、字符串中单词的翻转
两个字符串包含的字符是否完全相同
s = "anagram", t = "nagaram", return true.
s = "rat", t = "car", return false.
计算一组字符集合可以组成的回文字符串的最大长度
Input : "abccccdd"
Output : 7
Explanation : One longest palindrome that can be built is "dccaccd", whose length is 7.
字符串同构
Given "egg", "add", return true.
Given "foo", "bar", return false.
Given "paper", "title", return true.
回文子字符串个数
class Solution {
public:
int countSubstrings(string s) {
/*
思路:使用动态规划的思路,字符串s[i:j]是否为回文字符串取决于s[i+1:j-1]和s[i]==s[j]的结果
时间复杂度:O(n^2)
空间复杂度:O(n^2)
*/
int dp[1005][1005];
memset(dp, 0, sizeof(dp));
int ans = 0;
for(int diff = 0; diff < s.size(); diff++){
for(int i = 0;i+diff < s.size(); i++){
int j = i+diff;
if(i == j)
dp[i][j] = 1;
else if(s[i] == s[j]){
if(j == i+1)
dp[i][j] = 1;
else
dp[i][j] = dp[i+1][j-1];
}
ans += dp[i][j];
}
}
return ans;
}
};
统计二进制字符串中连续 1 和连续 0 数量相同的子字符串个数
class Solution {
public:
int countBinarySubstrings(string s) {
/*
思路:遇到01或者10开始向左右延伸,计算目标字符串的个数
时间复杂度:O(n^2)
空间复杂度:O(1)
*/
int n = s.size();
int ans = 0;
for(int i = 0;i < n-1; i++){
if(s[i] != s[i+1]){
int left = i, right = i+1;
while(left >= 0 && right < n && s[left] == s[i] && s[right] == s[i+1]){
ans += 1;
left -= 1;
right += 1;
}
}
}
return ans;
}
};
子序列计数
典型的动态规划,在s中找t,定义dp[i][j]
为在s[:j]
中找到t[:i]
的个数
class Solution {
public:
int numDistinct(string s, string t) {
if(t.size() > s.size())
return 0;
long long unsigned dp[1005][1005];
memset(dp, 0, sizeof(dp));
for(int i = 1;i <= t.size(); i++){
for(int j = 1;j <= s.size(); j++){
if(i == 1 && j == 1)
dp[i][j] = (t[i-1] == s[j-1]);
if(t[i-1] == s[j-1]){
if(i == 1)
dp[i][j] = dp[i][j-1] + 1;
else
dp[i][j] = dp[i][j-1] + dp[i-1][j-1];
}
else
dp[i][j] = dp[i][j-1];
//cout << dp[i][j] << " " << t[i-1] << s[j-1];
}
//cout << endl;
}
return dp[t.size()][s.size()];
}
};
最短的回文字符串
解题思路:本题要求是在s的左边填充字符使得填充后的字符串为回文字符串,要求输出满足要求的最短的字符串。本题的求解目标可转化为求s从左边开始最长的回文子字符串,假设为s[:k],则只需要在左边填充s[k+1:]反序后的结果即为所求的回文字符串,为什么s从左边开始最长的回文字符串即为所求,可用反证法证明。
现在问题是怎么求解s从左边开始的最长的回文字符串s[:k],一种简单的方法就是从左到右扫描,时间复杂度O(n^2),不满足要求,虽然表面上问题是回文串的判断,但事实上是字符串匹配问题,只是匹配的源字符串和目标字符串在一个字符串中,所以就想到使用KMP算法,KMP算法具体介绍见链接,回文字符串的判定需要对KMP算法进行一点改动,具体见代码。
class Solution {
public:
bool check(string s, int i, int j){
while(i < j && s[i] == s[j]){
i += 1;
j -= 1;
}
return i >= j;
}
string shortestPalindrome(string s) {
if(s.size() == 0)
return s;
// O(n^2)找从左边开始的最长的回文子串
// for(int j = s.size()-1; j >= 0; j -= 1){
// if(check(s, 0, j)){
// maxIndex = j+1;
// break;
// }
// }
// O(n)找从左边开始的最长的回文子串
vector<int> next_arr(s.size(), 0);
for(int i = 1;i < s.size(); i++){
if(s[i] == s[next_arr[i-1]])
next_arr[i] = next_arr[i-1] + 1;
else{
int tmp = next_arr[i-1];
while(s[i] != s[tmp] && tmp != 0)
tmp = next_arr[tmp-1];
next_arr[i] = tmp == 0 ? (s[i] == s[0]) : tmp;
}
}
int i = 0, j = s.size()-1;
int maxIndex = 1;
while(j > 0){
if(s[i] == s[j]){
i += 1;
j -= 1;
}
else if(i == 0)
j -= 1;
else { // i!=0
i = next_arr[i-1];
}
if(i >= j){
// cout << i << " " << j << endl;
maxIndex = j + i + 1;
break;
}
}
string pad = s.substr(maxIndex, s.size()-maxIndex);
i = 0;
j = pad.size()-1;
while(i < j){
swap(pad[i], pad[j]);
i += 1;
j -= 1;
}
return pad + s;
}
};