题意:用原串 s 操作生成目标串 t(开始为空串):将 s 串拼接到 t 串末尾,然后从 s 串中选一个字母从 s 串中全部删掉。执行上述操作直至 s 串为空,现在给出最终生成的目标串 t,请判断能够用某个 s 串生成,不能输出 -1,否则输出 s 串以及删除字符的顺序
1 2 3 4 5 6
Input: abacabaaacaac Output: abacaba bac //consider s="abacaba" so the actions may be performed as follows: //t="abacaba", the letter 'b' is selected, then s="aacaa"; //t="abacabaaacaa", the letter 'a' is selected, then s="c"; //t="abacabaaacaac", the letter 'c' is selected, then s="" (the empty string).
思路:倒着看一连串相同的字母一定是最后删除的,依次类推第二个一连串相同且没出现过的字母一定是倒数第二个删除的,那么我们就可以这样计算出每个字母拼接所贡献的次数,再统计一下每个字母在整个 t 串中的数量。两者相除就是字母在 s 串中的个数,所有字母的个数相加进而求出 s 串长度 len。又因为 t 串包含 s 串所以我们直接取 t 串中前 len 个字符作为 s 串,再按照规则模拟一遍看 s 串能够生成 t 串,不能说明矛盾输出 -1,否则输出答案
int n, k; inlineboolcheck(int x){ //检查 vector<bool> vec(10); while (x) { //修改位置到前面出现的不同数字 vec[x % 10] = true; x /= 10; } int cnt = 0; for (auto e : vec) cnt += e; return cnt <= k; } inlineintgetans(int x, int pos){ //凑数 int ans = x; vector<bool> vec(10); while (x) { vec[x % 10] = true; x /= 10; } int cnt = 0; for (auto e : vec) cnt += e; //修改位置后面的数可以随便填 if (vec[0] || cnt < k) { //如果前面有0或者还可以有不同的数后面的数直接用0填充 while (pos--) ans = ans * 10; return ans; } //否则后面的数只能用前面最小的数填充 int minn = 0; for (int i = 9; i >= 0; --i) if (vec[i]) minn = i; while (pos--) ans = ans * 10 + minn; return ans; } voidtest_case(){ cin >> n >> k; int tmp = n; for (int i = 0; i < 10; ++i, tmp /= 10) { //枚举当前位置作为最靠前的修改位置 int now = tmp % 10; if (i != 0) ++now; //确定当前位置修改的最小范围 for (int j = now; j < 10; ++j) { //枚举尝试修改的值 if (check(tmp / 10 * 10 + j)) { cout << getans(tmp / 10 * 10 + j, i) << endl; return; } } } }