dfs回溯暴力处理题——累加数

Posted C+++++++++++++++++++

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了dfs回溯暴力处理题——累加数相关的知识,希望对你有一定的参考价值。

题目


题目链接

题目详解

这题刚开始我也是一脸懵逼,毕竟好久没做题用直接的暴力法了,一时间暴力法都没想出来该怎么做。此时非常想去翻题解,但我最终还是压抑住了这股翻题解的心思,给自己定了个时,20分钟之内还没写出思路来,就翻题解。结果竟然是20min之内写出了做题的大概思路和代码框架。然后就有了本篇题解!

首先把题目读懂:

  1. 题目要求这个字符串必须是从头到尾存在三个数:s1+s2=s3。
  2. 注意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++算法思路:

  1. 通过dfs的每一层级把三个字符串划分为一组,那么如何枚举字符串呢?通过枚举字符串的长度即可,然后再根据 substr 便可达到枚举字符串的的目的。
  2. 注意小细节:
    一、为了防止加法溢出,不能直接转整数,而需要字符串模拟大整数加法。
    二、需要判断每个枚举出的字符串是否有效(不能有前缀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回溯暴力处理题——累加数的主要内容,如果未能解决你的问题,请参考以下文章

数独1--暴力回溯法(时间超)

深入浅出回溯算法

CodeForces 550B Preparing Olympiad(DFS回溯+暴力枚举)

每日一题小和问题

每日一题小和问题

每日一题小和问题