一个字符串str分割成k个子串,字串要全部是回文数,需要改变的字符个数
例如,字符串 abcbf分割成 3个字串 可以分割成 a,bcb,f这样 不需要改变字符就可以实现。
又例如字符串 abfa分割成 3个字串 可以分割成 ab,f,a需要改变 1个字符,变成 aa,f,a 就可以实现。
这就是这道题的意思。
首先我们采用一种傻瓜式的方式来解决这个问题(这里仅仅以 k=3为例)
我们的想法是先将所有可以分割的结果 列举出来,再 分别计算每种分割方法下的改变字符串的个数,求 最小值即为解。
例如我们提到的字符串 “abcbf”
它所有的分割方式如下:
a,b,cbf 需要修改 1个
a,bc,bf 需要修改 2个
a,bcb,f 需要修改 0个
ab,c,bf 需要修改 2个
ab,cb,f 需要修改 2个
abc,b,f 需要修改 1个
这样的话我们求其最小值0就是我们想得到的结果。
代码如下:
public static ArrayList<String > Process(String str)
{
//这里以k=3为实例
//该函数可以求str分为三个字串的所有可能
ArrayList<String> list=new ArrayList<String>();
for(int i=1;i<str.length()-1;i++)
{
for(int j=i+1;j<str.length();j++)
{
String cur=str.substring(0,i)+","+
str.substring(i,j)+","+
str.substring(j,str.length());
list.add(cur);
}
}
return list;
}
public static class ReturnType
{
int min;//min代表最小改变数
String str;//最小改变数对应的字符串
public ReturnType(int max, String str) {
this.min = max;
this.str = str;
}
}
public static ReturnType GetNum1(String str,int k)
{
ArrayList<String> list=Process(str);
int min=Integer.MAX_VALUE;
String maxString="";
String[] cur=new String[3];
for(String s:list)
{
// System.out.print(s+" ");
cur=s.split(",");
int times=0;
for(int i=0;i<cur.length;i++)
{
times+=changeTimes(cur[i], 0, cur[i].length()-1);
}
// System.out.println("需要修改"+times+"个");
min=Math.min(min, times);
if(min==times)
{
maxString=s;
}
}
return new ReturnType(min, maxString);
}
public static int changeTimes(String str,int left,int right)
{
int count=0;
while(left<right)
{
if(str.charAt(left)!=str.charAt(right))
{
count++;
}
left++;
right--;
}
return count;
}
这种方法由于空间和时间的损耗都比较大,所以当k和arr的长度增大时损耗极高。
因此我们提出了使用动态规划的方法。
对于一个dp表我们设i为当前字符串从0到i-1,j为要要划分的个数。当j>i时我们知道是没有意义的,显而易见,将个数为3的字符串划分成4个字串是不可以实现的。
而当j=1的时候我们可以直接计算字符串变成回文所需要改变的字符个数。
j>1假设我们要求dp[4][2]的结果,即将长度为4(字符从0-3)的串划分2个子串变成回文所需要改变的字符个数。那我们应该需要计算
将前面的1个字符分为1个的个数+后面3个字符分为一个的个数、
将前面的2个字符分为1个的个数+后面2个字符分为一个的个数、
将前面的3个字符分为1个的个数+后面1个字符分为一个的个数的最小值
即min(dp[1][1]+changeTimes(arr,1,3),
dp[2][1]+changeTimes(arr,2,3)
+dp[3][1]+changeTimes(arr,3,3));
代码如下:
public static int GetNum2(String str,int k)
{
if(str==null||str.length()<k)
{
return 0;
}
int[][] dp=new int[str.length()+1][k+1];
for(int i=0;i<dp.length;i++)
{
for(int j=0;j<dp[0].length;j++)
{
dp[i][j]=str.length();
//dp进行初始化,防止数组初始值0影响后续的计算结果。
}
}
//i代表从0到i的字符串
//j代表被分割的个数
for(int i=1;i<dp.length;i++)
{
for(int j=1;j<=Math.min(i, k);j++)
{
if(j==1)
{
dp[i][j]=changeTimes(str,0,i-1);
}
else
{
for(int m=j-1;m<i;m++)
{
dp[i][j]=Math.min(dp[i][j], dp[m][j-1]+changeTimes(str, m, i-1));
}
}
}
}
return dp[str.length()][k];
}