"Algorithm Competition·Quickly 300 Questions" One question per day: "Rainbow Number"

" Algorithm Competition: 300 Quick Questions " will be published in 2024 and is an auxiliary exercise book for "Algorithm Competition" .
All questions are placed in the self-built OJ New Online Judge .
Codes are given in three languages: C/C++, Java, and Python. The topics are mainly mid- to low-level topics and are suitable for entry-level and advanced students.


" Rainbow Number ", link: http://oj.ecustacm.cn/problem.php?id=1840

Question description

[Title description] Rainbow number: a decimal integer without leading 0, two adjacent numbers are different.
Given a lower bound and an upper bound, count the number of rainbows between them.
[Input format] The first line of input is a positive integer L, which represents the lower bound. The second line is a positive integer R, representing the upper bound.
1≤L≤R≤10^(100000). Note that the length of the number here may be up to 100000.
[Output format] Output a number to represent the answer. Since the answer is too large, it needs to be remainder 998244353.
【Input sample】

样例11
10

样例212345
65432

【Output sample】

样例110

样例235882

answer

  Since the range of the interval [L, R] is extremely large, direct violent verification of whether each number is a rainbow number will time out. In addition, because the number is too large, it is inconvenient to calculate directly by numbers. You can process large numbers with high precision and use the array num[] to store the large number. num[i] is the i-th digit of the large number.
  This question is related to digital statistics and is a relatively direct DP question on digital statistics. The code uses the dfs() template of "5.3.2 Memory Search Implementation of Digital Statistics DP" in "Algorithm Competition" [For the principles and two coding methods of digital statistics DP, see "Algorithm Competition", Tsinghua University Press, Luo Yongjun, Written by Guo Weibin, pages 333~338. The code for this question applies the template in the book. ], when counting numbers in dfs(), just exclude the rainbow number.
  Define dp[] as the number of rainbow numbers with leading zeros and unlimited digit restrictions, for example:
  dp[1], the number of rainbow numbers within 01~09, dp[1] = 9.
  dp[2], the number of rainbow numbers within 001~099, dp[2] = 81. Excluding the rainbow numbers 001, 002,..., 009, 011, 022,...099, a total of 18 are excluded, and dp[2] = 99-18 = 81.
  dp[3], the number of rainbow numbers within 0001~0999, corresponding to dp[3] = 729, excluding rainbow numbers 0001, 0002, 0099, 0100,…0111, 0112,…, 0999.
  Code steps:
  (1) Read L, R. Line 28 reads in L and R as strings.
  (2) solve(s) calculates the number of rainbows in the range [1, s], and the number of rainbows in [L, R] is solve®-solve(L-1). Since L is a number expressed in string form, lines 29 to 32 calculate in advance the string form of L-1.
  (3) In solve(s), first convert the large number represented by the string s into the array num[], and the large number is stored in num[1]~num[len]. For example, if you enter s = "65432", you will get num[1]~num[5] =2, 3, 4, 5, 6.
  (4) dfs() counts the number of rainbow numbers in [1, s], s is a large number represented by num[1]~num[len]. Line 13 accumulates the number of numbers without leading zeros, and line 14 removes the rainbow numbers.
  The complexity is explained below. The main task of dfs() is to calculate dp[], that is, to find dp[1] ~ dp[len]. The calculation amount is very small, about 10×len.
[Key Points] Digital statistics DP.

C++ code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int num[100005];        //存输入的数字
ll dp[100005];
ll MOD = 998244353;
ll dfs(int pos,int last,bool lead,bool limit){
    
                 //last: 上一位
    ll ans = 0;
    if(pos==0)   return 1;                                 //递归到0位数,结束返回
    if(!lead && !limit && dp[pos]!=-1)  return dp[pos];    //记忆化搜索
    int up=(limit ? num[pos] : 9);                         //这一位的最大值
    for(int i=0;i<=up;i++){
    
    
        if(i==0 && lead)   ans += dfs(pos-1,i,true, limit&&(i==up)); //lead=true,无前导0
        else if(last != i) ans += dfs(pos-1,i,false,limit&&(i==up)); //与上一位不同,排除彩虹数
        ans %= MOD;
    }
    if(!lead && !limit)    dp[pos] = ans;    //状态记录:有前导0,无数位限制
    return ans;
}
ll solve(string s){
    
    
    int len=0;
    for(int i=s.length()-1;i>=0;i--)   //把字符串转成数字,存入num[1]~num[len]
        num[++len]=(s[i]-'0');         //例如输入“65432”,得num[1:5]=2 3 4 5 6
    memset(dp,-1,sizeof(dp));
    return dfs(len,-1,true,true);
}
int main(){
    
    
    string L,R;   cin>>L>>R;
    for(int i=L.length()-1;i>=0;i--){
    
        //求L-1,例如L=12345,L-1=12344
        if(L[i]!='0') {
    
     L[i]--; break;}  //这一位不是0,减1即可
        else   L[i]='9';                 //这一位是0,减1得9
    }
    cout<<((solve(R)-solve(L))%MOD + MOD) % MOD;
    return 0;
}

Java code

import java.util.*;
public class Main {
    
    
    static long[] dp = new long[100005];
    static int[] num = new int[100005];
    static long MOD = 998244353;
    public static void main(String[] args) {
    
    
        Scanner scanner = new Scanner(System.in);
        String L = scanner.next();
        String R = scanner.next();
        StringBuilder resultL = new StringBuilder(L);
        for (int i = L.length() - 1; i >= 0; i--) {
    
    
            if (resultL.charAt(i) != '0') {
    
    
                resultL.setCharAt(i, (char)(resultL.charAt(i) - 1));
                break;
            } else {
    
    
                resultL.setCharAt(i, '9');
            }
        }
        System.out.println((solve(R) - solve(resultL.toString()) + MOD) % MOD);
    }
    public static long dfs(int pos, int last, boolean lead, boolean limit) {
    
    
        long ans = 0;
        if (pos == 0) return 1;
        if (!lead && !limit && dp[pos] != -1) return dp[pos];
        int up = (limit ? num[pos] : 9);
        for (int i = 0; i <= up; i++) {
    
    
            if (i == 0 && lead) ans += dfs(pos - 1, i, true, limit && (i == up));
            else if (last != i) ans += dfs(pos - 1, i, false, limit && (i == up));
            ans %= MOD;
        }
        if (!lead && !limit) dp[pos] = ans;
        return ans;
    }

    public static long solve(String s) {
    
    
        int len = 0;
        num = new int[100005];
        for (int i = s.length() - 1; i >= 0; i--) 
            num[++len] = s.charAt(i) - '0';
        Arrays.fill(dp, -1);
        return dfs(len, -1, true, true);
    }
}

Python code

   In solve(s), first convert the large number represented by the string s into the array num[], and the large number is stored in num[0]~num[len-1]. For example, if you enter s = "65432", you will get num[0]~num[4] =2, 3, 4, 5, 6.

import sys
sys.setrecursionlimit(100000)
 
MOD = 998244353
dp = [-1] * 100005
num = []
def dfs(pos, last, lead, limit):
    if pos == -1:   return 1     
    global dp
    global num 
    if not lead and not limit and dp[pos] != -1:   return dp[pos]     
    up = num[pos] if limit else 9
    ans = 0     
    for i in range(up + 1):
        if i == 0 and lead:  ans += dfs(pos - 1, i, True,  limit and (i == up))
        elif last != i:      ans += dfs(pos - 1, i, False, limit and (i == up))         
        ans %= MOD     
    if not lead and not limit:    dp[pos] = ans         
    return ans
 
def solve(s):
    global dp
    global num
    num = [int(c) for c in s[::-1]]  #把字符串转成数字,存入num[0]~num[len-1]
    dp = [-1] * 100005
    return dfs(len(num) - 1, -1, True, True)
 
L = input()
R = input()
L_minus_one = str(int(L) - 1)
result = (solve(R) - solve(L_minus_one) + MOD) % MOD
print(result)

Guess you like

Origin blog.csdn.net/weixin_43914593/article/details/132690816