纸牌拼接

题目描述

给定一个字符串target,给定一个字符数组strikers,出现的字符都是小写字母,strikers每一个字符串代表一个贴纸,你可以把单个字符剪开使用,目的是拼出target,返回至少需要多少张贴纸,(每种贴纸的数量无限)

分析

将每一种贴纸都当做第一个被选择的贴纸,然后根据贴纸更改target递归进行求解,从所有中选取所需纸牌最少的。
优化:每次得到target的方法是按照词频统计进行更新(因为其字母顺序无关)
使用map容器将每种target对应的方法数记录下来

代码

#include <iostream>
#include <cstring>
#include <limits>
#include <cstring>
#include <map>
using namespace std;
string strikers[100];
string target;
int n = 3;
int way1();
int process1(string target);
string strMinus(string s1, string s2);
int way2();

int process2(int scount[][26], string target);
int way3();
int process3(int scount[][26], string target, map<string, int> dp);
int main() {
    strikers[0] = "ab";
    strikers[1] = "c";
    strikers[2] = "abcd";
    target = "babac";
    cout << way1() << endl;
    cout << way2() << endl;
    cout << way3() << endl;
    return 0;
}
int way1() {
    int ans = process1(target);
    return ans == INT_MAX ? -1 : ans;
}
//所有贴纸无穷张,构成target所需的最小张数
int process1(string target) {
    if(target.size() == 0) {
        return 0;
    }
    int minNum = INT_MAX;
    for(int i = 0; i < n; i++) {
        string rest = strMinus(target, strikers[i]);
        if(rest.size() != target.size()) {
            minNum = min(minNum, process1(rest));
        }
    }
    return minNum + (minNum == INT_MAX ? 0 : 1);
}
string strMinus(string s1, string s2) {
    char ch1[200];
    int countNum[26], cnt = 0;
    for(int i = 0; i < 26; i++) {
        countNum[i] = 0;
    }
    for(int i = 0; i < s1.size(); i++) {
        countNum[s1[i] - 'a']++;
    }
    for(int i = 0; i < s2.size(); i++) {
        countNum[s2[i] - 'a']--;
    }
    for(int i = 0; i < 26; i++) {
        if(countNum[i] > 0) {
            for(int j = 0; j < countNum[i]; j++) {
                ch1[cnt++] = (char)(i + 'a');
            }
        }
    }
    ch1[cnt++] = '\0';
    string str = ch1;
    return str;
}
int way2() {
    int scount[n][26];
    memset(scount, 0, sizeof(scount));
    //对strikers进行字符统计
    for(int i = 0; i < n; i++) {
        for(int j = 0; j < strikers[i].size(); j++) {
            scount[i][strikers[i][j] - 'a']++;
        }
    }
    return process2(scount, target);
}
//根据词频统计表进行相减操作
//选取含有target的第一个字母的纸牌,以进行剪枝
int process2(int scount[][26], string target) {
    if(target.size() == 0) {
        return 0;
    }
    int tcount[26];
    memset(tcount, 0, sizeof(tcount));
    for(int i = 0; i < target.size(); i++) {
        tcount[target[i] - 'a']++;
    }
    int minNum = INT_MAX;
    for(int i = 0; i < n; i++) {
        int *sticker = scount[i];
        //剪枝,包含target的第一个字符
        if(sticker[target[0] - 'a'] > 0) {
            char ch1[100];
            int cnt = 0;
            for(int j = 0; j < 26; j++) {
                if(tcount[j] > 0) {
                    int num = tcount[j] - sticker[j];

                    for(int k = 0; k < num; k++) {
                        ch1[cnt++] = (char)(j + 'a');
                    }
                }
            }
            ch1[cnt++] = '\0';
            string rest = ch1;
            minNum = min(minNum, process2(scount, rest));
        }
    }
    return minNum + (minNum == INT_MAX ? 0 : 1);
}
int way3() {
    int scount[n][26];
    memset(scount, 0, sizeof(scount));
    //对strikers进行字符统计
    for(int i = 0; i < n; i++) {
        for(int j = 0; j < strikers[i].size(); j++) {
            scount[i][strikers[i][j] - 'a']++;
        }
    }
    map<string, int> dp;
    dp[""] = 0;
    return process3(scount, target, dp);
}
int process3(int scount[][26], string target, map<string, int> dp) {
    if(dp.find(target) != dp.end()) {
        return dp[target];
    }
    int tcount[26];
    memset(tcount, 0, sizeof(tcount));
    for(int i = 0; i < target.size(); i++) {
        tcount[target[i] - 'a']++;
    }
    int minNum = INT_MAX;
    for(int i = 0; i < n; i++) {
        int *sticker = scount[i];
//        剪枝,包含target的第一个字符
//        if(sticker[target[0] - 'a'] > 0) {
//            char ch1[100];
//            int cnt = 0;
//            for(int j = 0; j < 26; j++) {
//                if(tcount[j] > 0) {
//                    int num = tcount[j] - sticker[j];
//
//                    for(int k = 0; k < num; k++) {
//                        ch1[cnt++] = (char)(j + 'a');
//                    }
//                }
//            }
//            ch1[cnt++] = '\0';
//            string rest = ch1;
//            minNum = min(minNum, process3(scount, rest, dp));
//        }
            //剪枝,包含target的第一个字符
            if(scount[i][target[0] - 'a'] > 0) {
                string rest  = "";
                for(int j = 0; j < 26; j++) {
                    if(tcount[j] > 0) {
                        rest += string(tcount[j] - scount[i][j],j + 'a');
                    }
                }
                minNum = min(minNum, process3(scount, rest, dp));
            }
    }
    int ans = minNum + (minNum == INT_MAX ? 0 : 1);
    dp[target] = ans;
    return ans;
}

注意事项

在之前习惯了使用全局变量,而其初始化为0,当在函数中声明数组时,需要手动初始化。

posted @ 2022-07-14 16:20  sakuzeng  阅读(54)  评论(0)    收藏  举报