回溯法轻松实现回文字符串切割

目录

 

一、简介

二、题目

三、实战

1、判断一个字符串是否是回文串

2、回溯法实现

3、效率优化

四、总结


一、简介

     回溯法(探索与回溯法)是一种选优搜索法,又称为试探法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。回溯法很有趣可以解决一系列的问题,比如子集问题、排列问题以及八皇后问题等。是个很值得研究和学习的算法。

二、题目

给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。返回 s 所有可能的分割方案。

示例:

输入: "aab"
输出:["aa","b"]和 ["a","a","b"]

三、实战

1、判断一个字符串是否是回文串

     这个可以暴力求解

    

bool isPalindrome(std::string str2, int start, int end)
{

	const char*str = str2.c_str();
	int ns = end - start;
	ns = ns / 2;
	for (int i = 0; i < ns; ++i)
	{
		if (str[i+start] != str[end - i - 1])
		{
			return false;
		}

	}


	return true;
}

2、回溯法实现

             回溯法其实我感觉就是在一颗树上进行深度优先搜索(dfs)。这棵树怎么组织是根据不同题目的,比如本文的题目我把它的树的结构画出来了。dfs遍历这棵树就可以,然后通过条件进行一些剪枝,最后把需要的结果保存起来即可。

                                 

                                                                        图 1

void Palindrome(std::string &str, int start, int end, std::vector<std::string> &vecstr, std::vector<std::vector<std::string>> &vecres)
{
	int n = str.size();

	if (start == end)
	{
		vecres.push_back(vecstr);
		return;
	}
	
	for (int i = start; i < end; ++i)
	{
		if (isPalindrome(str, start, i+1))
		{
			vecstr.push_back(str.substr(start, i+1-start));
			Palindrome(str, i + 1, end, vecstr,vecres);
			vecstr.pop_back();
			

		}
	}
}

        vecstr用于记录dfs路径上的节点,符号条件的都会搜索到最后start==end,不符号条件的不会进行到最后。比如{}->a->a->b->end,这条路径会执行到start==end。而{}->a->ab这个路径是不行的,ab它不是回文串,不会执行到最后的start==end。  vecres用于记录结果,符号条件的结果存到vecres即可。isPalindrome就是用来剪枝的。

3、效率优化

     isPalindrome调用次数太多,可以用一个二维数组dp[i][j]来表示第j位置到第i位置的字符串是否是回文串。我这里使用了一点动态规划去求解dp数组,其实也可以双重循环暴力求解dp数组。

dp状态方程

dp[i][j] = dp[i-1][j+1]      : str[i]==str[j]

dp[i][j]=true                    :str[i]==str[j]  && (i-j)<=2

   

	for (int i = 0; i < pstr.length(); ++i)
	{
		for (int j = 0; j <= i; ++j)
		{
			if (str[j] == str[i] && ((i - j) <= 2 || dp[i - 1][j + 1]))
			{
				dp[i][j] = true;
			}
		}
	}
	
void Palindrome_dp(std::string &str, int start, int end, std::vector<std::string> &vecstr, std::vector<std::vector<std::string>> &vecres,bool** pdp)
{
	int n = str.size();

	if (start == end)
	{
		vecres.push_back(vecstr);
		return;
	}

	for (int i = start; i < end; ++i)
	{
		//if (isPalindrome(str, start, i + 1))
		if (pdp[i][start])
		{
			vecstr.push_back(str.substr(start, i + 1 - start));
			Palindrome(str, i + 1, end, vecstr, vecres);
			vecstr.pop_back();


		}
	}
}

四、总结

       算法都会对应一个数据结构,把算法对应的数据结构弄明白了,问题就easy了。回溯法就是对应这一颗树,然后在这棵树上进行dfs,再根据题目进行剪枝,在dfs过程中把结果保存。底下我会写一系列的关于回溯法的文章。

    

猜你喜欢

转载自blog.csdn.net/kupe87826/article/details/106529463