纸牌拼接
题目描述
给定一个字符串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,当在函数中声明数组时,需要手动初始化。