T5 最长回文子串
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为1000。
示例 1:
输入: “babad”
输出: “bab”
注意: “aba”也是一个有效答案。
示例 2:
输入: “cbbd”
输出: “bb”
传统的验证回文串的方法就是两个两个的对称验证是否相等,那么对于找回文字串的问题,就要以每一个字符为中心,像两边扩散来寻找回文串,这个算法的时间复杂度是O(n*n)
要注意奇偶情况,由于回文串的长度可奇可偶,比如”bob”是奇数形式的回文,”noon”就是偶数形式的回文,两种形式的回文都要搜索
动态规划
我们维护一个二维数组dp,其中dp[i][j]表示字符串区间[i, j]是否为回文串,
(1)当i = j时,只有一个字符,肯定是回文串,
(2)如果i = j + 1,说明是相邻字符,此时需要判断s[i]是否等于s[j],
(3)如果i和j不相邻,即i - j >= 2时,除了判断s[i]和s[j]相等之外,
dp[j + 1][i - 1]若为真,就是回文串,通过以上分析,可以写出递推式如下:
dp[i,j] = 1 if i == j
dp[i,j] = s[i] == s[j] if j = i + 1
dp[i,j] = s[i] == s[j] && dp[i + 1][j - 1] if j > i + 1
这里有个有趣的现象就是如果我把下面的代码中的二维数组由int改为
vector< vector < int> >后,就会超时,这说明int型的二维数组访问执行速度完爆std的vector啊,所以以后尽可能的还是用最原始的数据类型吧。
class Solution {
public:
string longestPalindrome(string s) {
int n = s.size();
int dp[n][n] = {0};
int left=0, right=0, len=0;
for(int i=0; i<n; i++)
{
dp[i][i] = 1; // 一个字符肯定是回文
for(int j=0; j<i; j++)
{
dp[j][i] = {s[j]==s[i] && (i-j<2 || dp[j+1][i-1])}; // 动态规划,前一个字串也是回文串
if(dp[j][i] && len<i-j+1) // 之前回文串短于当前回文串
{
len = i-j+1;
left = j;
right = i;
}
}
}
return s.substr(left, right-left+1);
}
};
T96 不同的二叉搜索树
给定一个整数 n,求以 1 … n 为节点组成的二叉搜索树有多少种?
这道题实际上是卡塔兰数的不像斐波那契数那样人尽皆知
我们先来看当 n = 1的情况,只能形成唯一的一棵二叉搜索树,n分别为1,2,3的情况如下所示:
class Solution {
public:
int numTrees(int n) {
vector<int> dp(n+1,0);
dp[0]=1;
dp[1]=1;
for(int i=2; i<=n; i++)
{
for(int j=0; j<i; j++)
dp[i] = dp[i] + dp[j]*dp[i-j-1];
}
return dp[n];
}
};
T392 判断子序列
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
示例 1:
s = “abc”, t = “ahbgdc”
返回 true.
示例 2:
s = “axc”, t = “ahbgdc”
返回 false.
这道题算比较简单的一种,我们可以用两个指针分别指向字符串s和t,然后如果字符相等,则i和j自增1,反之只有j自增1,最后看i是否等于s的长度,等于说明s已经遍历完了,而且字符都有在t中出现过
class Solution {
public:
bool isSubsequence(string s, string t) {
if(s.empty()) return true;
int i=0,j=0;
while(i<s.size() && j<t.size())
{
if(s[i]==t[j])
{
++i;
++j;
}
else
++j;
}
return i==s.size();
}
};
T647 回文字串
给定一个字符串,你的任务是计算这个字符串中有多少个回文子串。
具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被计为是不同的子串。
示例 1:
输入: “abc”
输出: 3
解释: 三个回文子串: “a”, “b”, “c”.
示例 2:
输入: “aaa”
输出: 6
说明: 6个回文子串: “a”, “a”, “a”, “aa”, “aa”, “aaa”.
需要一个二维数组来记录子字符串[i, j]是否是回文串,那么我们直接就将dp[i][j]定义成子字符串[i, j]是否是回文串就行了,然后我们i从n-1往0遍历,j从i往n-1遍历,然后我们看s[i]和s[j]是否相等,这时候我们需要留意一下,有了s[i]和s[j]相等这个条件后,i和j的位置关系很重要,如果i和j相等了,那么dp[i][j]肯定是true;如果i和j是相邻的,那么dp[i][j]也是true;如果i和j中间只有一个字符,那么dp[i][j]还是true;如果中间有多余一个字符存在,那么我们需要看dp[i+1][j-1]是否为true,若为true,那么dp[i][j]就是true。赋值dp[i][j]后,如果其为true,结果res自增1,参见代码如下:
class Solution {
public:
int countSubstrings(string s) {
int n=s.size();
int res=0;
vector<vector<bool>> dp(n,vector<bool>(n,false));
for(int i=n-1; i>=0; i--)
{
for(int j=i; j<n; j++)
{
dp[i][j] = (s[i]==s[j]) && (j-i<=2 || dp[i+1][j-1]);
if(dp[i][j])
++res;
}
}
return res;
}
};