HDU - 3886 Final Kichiku “Lanlanshu”(数位DP,各种形状模板)

Final Kichiku “Lanlanshu”


Problem Description
During 2010 summer training, temperlisyer often does problem like this:
“Consider a decimal integer as sequence of digits {D 0, D 1 … D n-1} (D 0 > 0), if exists such x, y and z, satisfying:
1.D i-1<D i (0<i<=x)
2.D i-1=D i (x<i<=y)
3.D i-1<D i (y<i<=z)
4.D i-1>D i (z<i<=n-1)

We call this integer “Lanlanshu”, now give you two numbers A and B, calculate how many “Lanlanshu” are in [A, B].“
He solved so many of these and finally get bored, and then get crazy! He decided to make up a problem to put this type of problems to an end.
Give you a string str consists only by ‘/’, ‘-‘ and ‘\’, and its length is l. Consider a decimal integer as sequence of digits {D 0, D 1 … D n-1} (D0 > 0), define x 0=0, x l=n-1, if exists such x 1, x 2...x l (x 0 < x 1 < x 2 < ... < x l) satisfying:
1. If str[i]=’/’, D j-1<D j (x i<j<=x i+1)
2. If str[i]=’-’, D j-1=D j (x i<j<=x i+1)
3. If str[i]=’\’, D j-1>D j (x i<j<=x i+1)

We call it Final Kichiku “Lanlanshu”, now give you two numbers A and B, calculate how many Final Kichiku “Lanlanshu” are in [A, B]. This number maybe huge, we only want to now the last 8 digits of the result.
 

Input
Multiple cases (no more than 100), for each case:
The first line is string str, length is below 100.
The second line contains two integers A and B (0≤Ai≤Bi≤10^100).
Input terminates by EOF.
 

Output
For each case, output 8 digits representing the last 8 digits of the number of Final Kichiku “Lanlanshu” in [A, B]. If it’s less than 8 digits, fill it with leading zeros.
 

Sample Input
  
  
/\ 01221 2012
 

Sample Output
  
  
00000315
 

Author
temperlsyer
 

Source
 

题意:给你一个模式串,找到区间内符合该模式的数字的个数。


解题思路:数位DP,好题!以后类似的题直接套这个模板就行了!代码有详细注释,很好理解。


#include<iostream>
#include<deque>
#include<memory.h>
#include<stdio.h>
#include<map>
#include<string.h>
#include<algorithm>
#include<vector>
#include<math.h>
#include<stack>
#include<queue>
#include<set>
#include<bitset>
using namespace std;
typedef long long int ll;
const int MOD=100000000;

int dp[110][110][10];//第i位,前j个字符已经符合,前一个数是k的个数
int dig[200];

string str;//模式串

//去除前导0
string qu(string a){
    string ans;
    bool have=0;
    for(int i=0;i<a.size();i++){
        if(!have&&a[i]=='0')
            continue;
        have=1;
        ans.push_back(a[i]);
    }
    return ans;
}

//字符串减一操作
string subone(string a){
    int len=a.size();
    if(a[len-1]>'0'){
        a[len-1]-=1;
        return a;
    }
    else{
        int i=len-1;
        while(i>=0&&a[i]<='0')
            a[i--]='9';
        a[i]-=1;
        return a;
    }
}

//判断两个字符是否符合当前位的模式串
int ok(int x,int y,char op)
{
    if(op=='/')return x<y;
    if(op=='-')return x==y;
    return x>y;
}


//当前处理到第pos个字符,模式串匹配到了len,前一个数字是last,是否前导0
int dfs(int pos,int len,int last,bool have,bool limit){
    
    if(pos==0)//要判断模式串是否也匹配完
        return len==str.size();
    
    //记忆化搜索
    if(!limit && !have && dp[pos][len][last]!=-1){
        return dp[pos][len][last];
    }
    
    int end=limit?dig[pos]:9;
    int ans=0;
    
    for(int i=0;i<=end;i++){
        if(have){
            ans+=dfs(pos-1,0,i,have&&i==0,limit&&i==end);//如果有前导0
        }
        else{
            
            if(len<str.size()&&ok(last,i,str[len]))
                ans+=dfs(pos-1,len+1,i,have&&i==0,limit&&i==end);//贪心的往后匹配,防止\这种串的干扰
            else{
                if(len>0&&ok(last,i,str[len-1]))
                    ans+=dfs(pos-1,len,i,have&&i==0,limit&&i==end);//往后不符合,再看看现在的符不符合
            }
            
        }
        
    }
    ans%=MOD;
    
    if(!limit&&!have)
        dp[pos][len][last]=ans;
    
    return ans;
    
}

int solve(string x){
    
    int pos=0;
    
    for(pos=0;pos<x.size();pos++)
        dig[x.size()-pos]=x[pos]-'0';
    
    return dfs(pos,0,0,1,1);
}


int main(){
    
    while(cin>>str){
        memset(dp,-1,sizeof(dp));
        string l,r;
        cin>>l>>r;
        l=qu(l);
        r=qu(subone(r));
        
        printf("%08d\n",(solve(r)-solve(l)+MOD)%MOD);
    }
    
    return 0;
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值