题目不难,但是用到了区间DP的思想。(我是这样用的),顺便复习以下区间dp
for(int len=2;len<=n;len++){
for(int l = 0; l+len-1<n; l++){
int r = l + len - 1;
....动态规划状态转移方程。
}
}
解释一下上面:
区间dp的更新是从小区间扩展到大区间,和动态规划的思想类似,有了小的区间上的值,就可以在这个小的区间上的值根据状态转移方程求得大区间上的值。
区间dp 的写法就是 第一重循环写区间长度, 第二重循环写区间左端点。
这里的 l 就是区间左端点。那这个for循环里 l + len - 1 是怎么来的呢? 为什么是r = l + len - 1呢?
相信推理能力强的小伙伴已经有答案了,for循环里的的l + len - 1就是指的右区间端点值。我们看有没有越界,看的就是右端点。那为什么l + len - 1是右端点呢?
考虑一个非常简单的例子,从2~5之间的左端点是2,右端点是5,一共2 3 4 5 共4个元素,但是呢,右端点减左端点=5-2=3,所以还需要加1才是区间长度:即r - l + 1 = len。所以呢,右端点就是r = l + len - 1。懂了吗?
这个题状态转移方程也是比较好定义的。
题目问是回文串的个数,然后单个的也算回文串,那毫无疑问,我们要设置一个二维的dp。
怎么考虑呢?
f[i][j] 表示什么含义呢?
我们想回文串,一个串是回文的,向外扩展怎么保证他还是回文的呢?那肯定要两个端点处的也是回文串才行。
也就是说 i~j是回文串当且仅当i+1~j-1是回文串且 s.charAt(i) == s.charAt(j)
。(为什么是i+1~j-1,你要两边都往里缩,那肯定要都往里了,左端点往右,右端点往左。
)
所以不妨这样考虑:
设状态转移方程f[l][r] 表示l~r之间是回文串。
所以状态转移方程就是f[l][r] = f[l+1][r-1] && s.charAt(l) == s.charAt(r)
这样就结束了吗 ?
还没有。
你想一下,如果你的长度是2,那l + 1 = r。那也就是,l+1 = r, r- 1 = l,那你想一下f[l+1][r-1]是不是就变成了f[r][l]了?这还了得???所以,这里的dp状态更新的长度要从3开始。那长度为2的怎么办?
还能怎么办? 更新一下就完了! 怎么更新? 长度为2,是回文的,那不就是相邻两个相等吗??这不就解决了吗?
for(int i=0;i+1<s.length();i++)
if(s.charAt(i) == s.charAt(i+1))
f[i][i+1] = true;
class Solution {
public int countSubstrings(String s) {
// f[i][j] 表示下标i~j之间是回文子串
// f[i][j] = f[i-1][j+1] + 1 if(s[i]==s[j])
// = f[i-1][j+1];
boolean [][] f = new boolean [s.length()+1][s.length()+1];
for(int i=0;i<s.length();i++)
f[i][i]= true;
for(int i=0;i+1<s.length();i++)
if(s.charAt(i) == s.charAt(i+1))
f[i][i+1] = true;
int n = s.length();
for(int len=3;len<=s.length();len++) {
for(int l=0;l+len-1<s.length();l++){
int r = l + len - 1;
f[l][r] = f[l+1][r-1] && s.charAt(l) == s.charAt(r);
}
}
System.out.println("afaf");
int cnt = 0;
for(int i=0;i<s.length()+1; i++){
for(int j=0;j<s.length()+1;j++){
System.out.print(f[i][j] + " ");
if(f[i][j]) cnt++;
}
System.out.println();
}
System.out.println(cnt);
return cnt;
}
}