自己的暴力解法果真超时了,但是很开心有进步,能把自己脑子里的算法给顺利翻译出来了,比刚开始做第一题的感觉好多了,开心。
暴力解法:遍历
class Solution {
public String longestPalindrome(String s) {
char []a=s.toCharArray();
int maxCount=0;
String sub="";
int j=0;
for(int k=0;k<a.length;k++){
for(int i=k;i<a.length;i++){
for( j=i;j<a.length;j++){
int p=i;int q=j;
int flag=0;
while(p<=q){
if(a[p]!=a[q]){
flag=1;
break;
}
p++;
q--;
}
if(flag==0){
if((j-i+1)>=maxCount){
maxCount=j-i+1;
sub=s.substring(i,j+1);
}
}
}
}
}
return sub;
}
}
solution1
看了一眼solution1的大概,想出了一个分治的解法,复杂度为o(n2)
For example, S = “caba”, S’= “abac”.
The longest common substring between S and S’ is “aba”, which is the answer.
//运用动态规划,将s和s'的遍历结果存为矩阵。
c[i][j]表示原字符串s,从s[i]起,向前长度为c[i][j]的substring是回文数 .
这里a是原字符串,k是翻转的。
public int [][] matrix(char []a,char[]k){
int m=a.length;
int n=k.length;
int[][]c=new int[m+1][n+1];
int i=0,j=0;
for(i=0;i<=m;i++) c[i][0]=0;
for(i=0;i<=n;i++)c[0][i]=0;
for(i=1;i<=m;i++)
for(j=1;j<=n;j++){
if(a[i-1]==k[j-1]){
c[i][j]=c[i-1][j-1]+1;
}
else{
c[i][j]=0;
}
}
return c;
}
比如s="bfaa” s‘=“aafb”
矩阵为:
0 0 0 0 0
0 0 0 0 1
0 0 0 1 0
0 1 1 0 0
0 1 2 0 0
为了处理方便,
其中第0列和第0行都预先设为0,c[1…s.length][1…s’.length]才是真正的动态规划矩阵
相当于c[4][2]中的4对应s[3],2对应s’[1]
如果s[i-1]=s’[j-1],则c[i][j]=c[i-1][j-1]+1;否则,c[i][j]=0 (这个规律画几个矩阵试试就明白了)
其中最大数可能为最长的回文数.即 c[4][2]=2,表示从s的第4个字符(s[3])起,向前滑2个,“aa”(s[2],s[3])为最长回文数。
特殊情况1:
Let’s try another example: S = “aabfgbaa”, S′ = “aabgfbaa”.
The longest common substring between S and S′ is “aab”. Clearly, this is not a valid palindrome.
矩阵为:
0 0 0 0 0 0 0 0 0
0 1 1 0 0 0 0 1 1
0 1 2 0 0 0 0 1 2
0 0 0 3 0 0 1 0 0
0 0 0 0 0 1 0 0 0
0 0 0 0 1 0 0 0 0
0 0 0 1 0 0 1 0 0
0 1 1 0 0 0 0 2 1
0 1 2 0 0 0 0 1 3
此时最大的c[i][j]是c【9】【9】=3,表示baa是我们求的是最大回文数,但这恰巧是要讨论的特殊情况,baa它不是真的,所以到这里就要判断一遍是否是回文数.
int p=0;
int q=sub.length-1;
int flag=0;
while(p<=q){
if(sub[p]!=sub[q]){
flag=1;
break;
}
p++;
q--;
}
发现不是。继续处理。
这种情况下,最长回文数的范围就被缩小了,可能在baa中。
如何分析呢?
注意上述矩阵,最大c[i][j]所在的对角线\上的值表示子串baa的可能最大回文数。现在这个问题和原先的问题是一个结构和性质,所以可以采用递归的思路
if(flag==1)
{
return longestPalindrome(String.valueOf(sub));
}
else
return String.valueOf(sub);
特殊情况2:
发现又错了,因为还有"abcdbbfcba"这种情况没有考虑到。我们上述做法是解决了特殊情况1:真正的结果在子串的子串里。假如结果在中间那一部分呢?比如这个的“dbbf”中的“bb”才是正确答案。
其实中间这一部分的处理办法和前面都是一样的,还是递归。
我们主要要判断中间部分sub2和由上述的sub,哪个的最长回文数更长?
于是最后一块的处理变成了,谁长返回谁,注意要保证sub2不为空才能进行处理,所以要判断
if(s.length()-max-max>0),否则会数组越界。
if(flag==1)
{
if(s.length()-max-max>0)
return longestPalindrome(String.valueOf(sub)).length()>=longestPalindrome(String.valueOf(sub2)).length()?
longestPalindrome(String.valueOf(sub)):longestPalindrome(String.valueOf(sub2));
else
return longestPalindrome(String.valueOf(sub));
}
else
return String.valueOf(sub);
}
总结:
特殊情况1,包含了最简单的情况+“aabfgbaa”(aa藏在假的回文数baa里)
特殊情况2,补充了"abcdbbfcba"这种最长回文数在中间的情况。
class Solution {
//运用动态规划,将s和s'的遍历结果存为矩阵,c[i][j]表示原字符串s,从s[i]起,向前长度为c[i][j]的substring是回文数
public int [][] matrix(char []a,char[]k){
int m=a.length;
int n=k.length;
int[][]c=new int[m+1][n+1];
int i=0,j=0;
for(i=0;i<=m;i++) c[i][0]=0;
for(i=0;i<=n;i++)c[0][i]=0;
for(i=1;i<=m;i++)
for(j=1;j<=n;j++){
if(a[i-1]==k[j-1]){
c[i][j]=c[i-1][j-1]+1;
}
else{
c[i][j]=0;
}
}
return c;
}
public String longestPalindrome(String s) {
char []a=s.toCharArray();
for (int i=0;i<a.length/2;i++){
char temp=a[i];
a[i]=a[a.length-1-i];
a[a.length-1-i]=temp;
}
char []k=s.toCharArray();
int maxCount=0;
int [][]c=matrix(k,a);
int max=0;
int maxi=0,maxj=0;
for(int i=1;i<=k.length;i++){
for(int j=1;j<=a.length;j++){
if(c[i][j]>=max){
max=c[i][j];
maxi=i;
maxj=j;
}
}
}
char []sub=s.substring(maxi-max,maxi).toCharArray();
char [] sub2=new char[s.length()];
if(s.length()-max-max>0)
sub2=s.substring(max,s.length()-max).toCharArray();
int p=0;
int q=sub.length-1;
int flag=0;
while(p<=q){
if(sub[p]!=sub[q]){
flag=1;
break;
}
p++;
q--;
}
if(flag==1)
{
if(s.length()-max-max>0)
return longestPalindrome(String.valueOf(sub)).length()>=longestPalindrome(String.valueOf(sub2)).length()?
longestPalindrome(String.valueOf(sub)):longestPalindrome(String.valueOf(sub2));
else
return longestPalindrome(String.valueOf(sub));
}
else
return String.valueOf(sub);
}
}
Solution 2:Dynamic Programming
用一个n2的矩阵
初始化:整个矩阵初始化为0,表示false。对角线上的元素全为1,表示true(单个字母当然是回文数了)
这个矩阵的下三角对我们来说没有意义,就一直初始化为0
首先是特殊情况
这个规则对应是j-i==1,就是上三角紧挨着对角线的一条。
if(j-i==1){
if(a[i]==a[j])
c[i][j]=1;
}
下面是一般情况:
else if(c[i+1][j-1]==1&&a[i]==a[j]) c[i][j]=1;
整个动态规划的代码如下所示:
class Solution {
public String longestPalindrome(String s) {
char []a=s.toCharArray();
int [][]c=new int[a.length][a.length];
int i=0;
int j=0;
for(i=0;i<a.length;i++){
for(j=0;j<a.length;j++){
if(i==j)c[i][j]=1;
else c[i][j]=0;
}
}
for(i=a.length-1;i>=0;i--){
for(j=a.length-1;j>=0;j--){
if(i<j){
if(j-i==1){
if(a[i]==a[j])
c[i][j]=1;
}
else if(c[i+1][j-1]==1&&a[i]==a[j]) c[i][j]=1;
}
}
}
int max=-1;
int m=-1,n=-1;
for(i=0;i<a.length;i++){
for(j=0;j<a.length;j++){
if(c[i][j]==1){
if(j-i>max){
max=j-i;
m=i;
n=j;
}
}
}
}
if(n!=-1&&m!=-1){
String sub=s.substring(m,n+1);
return sub;
}
else
return "";
}
}
其实还可以优化空间复杂度O(n2)
,因为是这个矩阵真正用到的是上三角,所以可以用有序数对进行顺序存储,这样就只要O(n)的空间复杂度了
Approach 3: Expand Around Center
class Solution {
public String longestPalindrome(String s) {
if (s == null || s.length() < 1) return "";
char []a=s.toCharArray();
int i=0;
int max=0;
int core=0;
for(i=0;i<a.length;i++){
int len1=expandPali(a,i,i);
int len2=-1;
if(i+1<a.length)
len2=expandPali(a,i,i+1);
int maxlen=Math.max(len1,len2);
if(maxlen>max){
max=maxlen;
core=i;
}
}
String sub="";
int start = core - (max - 1) / 2;
int end = core + max / 2;
sub=s.substring(start,end+1);
return sub;
}
private int expandPali(char []a,int left, int right){
while(left>=0&&right<a.length&&a[left]==a[right]){
left--;
right++;
}
return right-left-1;
}
}