dfs回溯暴力处理题——累加数
Posted C+++++++++++++++++++
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了dfs回溯暴力处理题——累加数相关的知识,希望对你有一定的参考价值。
题目
题目详解
这题刚开始我也是一脸懵逼,毕竟好久没做题用直接的暴力法了,一时间暴力法都没想出来该怎么做。此时非常想去翻题解,但我最终还是压抑住了这股翻题解的心思,给自己定了个时,20分钟之内还没写出思路来,就翻题解。结果竟然是20min之内写出了做题的大概思路和代码框架。然后就有了本篇题解!
首先把题目读懂:
- 题目要求这个字符串必须是从头到尾存在三个数:s1+s2=s3。
- 注意s1、s2、s3的长度并未确定。
具体示例:
199100199
-> s1:1 s2:99 s3:100
-> s1:99 s2:100 s3:199
我们发现它是一个不断往后推动s1和s2的过程,也就是只要确定了s1和s2,就能开始枚举s3了,一旦满足 s1+s2=s3
那么s2和s3就能够继续往后推动变成新的s1和s2,然后再枚举s3,会发现这就是一个dfs回溯的过程,最终我们只要能够一直这样枚举到给定的 num
字符串的最后,也就是枚举到 s1和s2的位置 正好把num占满,则该字符串肯定是累加数!
C++算法思路:
- 通过dfs的每一层级把三个字符串划分为一组,那么如何枚举字符串呢?通过枚举字符串的长度即可,然后再根据
substr
便可达到枚举字符串的的目的。 - 注意小细节:
一、为了防止加法溢出,不能直接转整数,而需要字符串模拟大整数加法。
二、需要判断每个枚举出的字符串是否有效(不能有前缀0)
解题代码
大整数加法有两种模拟思路:
1.反转a、b再计算
2.从后往前计算再反转
第一种方式:
class Solution
public:
//TODO 大数的加减运算,判断s1+s2是否等于s3
bool check(string s1,string s2,string s3)
int maxN = s1.size()>s2.size()?s1.size():s2.size();
if(maxN>s3.size())
return false;
reverse(begin(s1),end(s1));
reverse(begin(s2),end(s2));
reverse(begin(s3),end(s3));
char cmp[55]0;
int up = 0;
int i=0;
for(;i<maxN;i++)
int a = i<s1.size()?s1[i]-'0':0;
int b = i<s2.size()?s2[i]-'0':0;
cmp[i] = (a+b+up)%10+'0';
up = (a+b+up)/10;
while(up)
cmp[i++] = up%10+'0';
up /= 10;
return s3==string(cmp);
bool isPreFixZero(const char* s)
return s[0]=='0'&&s[1]!=0;//TODO 判断数字是否合法(数字是否含有前缀0)
//TODO dfs回溯算法。
//三个参数:
//src为取材的序列
//start是当前3个数字取材的首位置
//s1和s2表示3个数字序列已经取好的前两个数字
//然后通过枚举第三个数字的长度从src取第三个数字
bool dfs(string& src,int start,string& s1,string& s2)
//TODO 当正好无法选取第三个序列时判断完成,整个序列是累加数!
if(start+s1.size()+s2.size()==src.size())
return true;
int maxLen = src.size()-start;
int len1 = s1.size();
int len2 = s2.size();
for(int k=1;k<=maxLen-len1-len2;k++)
string s3 = src.substr(start+len1+len2,k);
//TODO 判断取好的数字是否合法,且s1+s2是否等于s3,如果满足这两个条件则进入下一层选取序列
if(!isPreFixZero(s3.c_str())&&check(s1,s2,s3)&&dfs(src,start+len1,s2,s3))
return true;
return false;
bool isAdditiveNumber(string num)
int n = num.size();
//TODO 在这里枚举最开始的两个串的长度,来进行dfs
for(int i=1;i<=n;i++)
for(int j=1;j<=n-i;j++)
if(i+j>=n)
break;
string s1 = num.substr(0,i);
string s2 = num.substr(i,j);
if(!isPreFixZero(s1.c_str())&&!isPreFixZero(s2.c_str())&&dfs(num,0,s1,s2))
return true;
return false;
;
第二种方式:
class Solution
public:
bool isAdditiveNumber(string num)
int n = num.size();
//TODO 在这里枚举最开始的两个串的长度,来进行dfs
for(int i=1;i<=n;i++)
for(int j=1;j<=n-i;j++)
if(i+j>=n)
break;
string s1 = num.substr(0,i);
string s2 = num.substr(i,j);
if(!isPreFixZero(s1.c_str())&&!isPreFixZero(s2.c_str())&&dfs(num,0,s1,s2))
return true;
return false;
//TODO 大数的加减运算,判断s1+s2是否等于s3
bool check(string& s1,string& s2,string& s3)
int maxN = s1.size()>s2.size()?s1.size():s2.size();
if(maxN>s3.size())
return false;
int n1 = s1.size()-1,n2 = s2.size()-1;
string ret;ret.reserve(40);
int up = 0;
int i=0;
for(;n1>=0||n2>=0;--n1,--n2)
int a = n1>=0?s1[n1]-'0':0;
int b = n2>=0?s2[n2]-'0':0;
ret += (a+b+up)%10+'0';
up = (a+b+up)/10;
if(up) ret += up+'0';
reverse(begin(ret),end(ret));
return s3==ret;
bool isPreFixZero(const char* s)
return s[0]=='0'&&s[1]!=0;//TODO 判断数字是否合法(数字是否含有前缀0)
//TODO dfs回溯算法。
//三个参数:
//src为取材的序列
//start是当前3个数字取材的首位置
//s1和s2表示3个数字序列已经取好的前两个数字
//然后通过枚举第三个数字的长度从src取第三个数字
bool dfs(string& src,int start,string& s1,string& s2)
//TODO 当正好无法选取第三个序列时判断完成,整个序列是累加数!
if(start+s1.size()+s2.size()==src.size())
return true;
int maxLen = src.size()-start;
int len1 = s1.size();
int len2 = s2.size();
for(int k=1;k<=maxLen-len1-len2;k++)
string s3 = src.substr(start+len1+len2,k);
//TODO 判断取好的数字是否合法,且s1+s2是否等于s3,如果满足这两个条件则进入下一层选取序列
if(!isPreFixZero(s3.c_str())&&check(s1,s2,s3)&&dfs(src,start+len1,s2,s3))
return true;
return false;
;
以上是关于dfs回溯暴力处理题——累加数的主要内容,如果未能解决你的问题,请参考以下文章
CodeForces 550B Preparing Olympiad(DFS回溯+暴力枚举)