一个字符串str分割成k个子串,字串要全部是回文数,需要改变的字符个数

一个字符串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-1j为要要划分的个数。当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];
	}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值