当前位置:网站首页>拼接字符串,得到字典序最小的结果
拼接字符串,得到字典序最小的结果
2022-07-01 16:26:00 【明朗晨光】
1、题目
给定一个由字符串组成的数组 strs,必须把所有的字符串拼接起来,返回所有可能的拼接结果中,字典序最小的结果。
2、思路
贪心策略:将数组中的字符串按字典序进行排序,然后按序拼接起来。
这种策略不对!!,因为存在反例:["b", "ba"],字典序 b < ba,按照贪心策略得到的拼接字符串应该就是 bba,但是实际上 bab 这个拼接的结果是比 bba 小的。
//失败的贪心策略
if (str1 < str2) //str1的字典序 < str2的字典序
str1在前;
else
str2在前;
真正有效的贪心策略:使用比较器判断两个字符串拼接后的字典序
//如果 str1 拼接上 str2 之后的字典序 < str2 拼接上 str1 之后的字典序
if (str1.str2 < str2.str1)
str1在前;
else
str2在前;
为什么这种贪心策略是有效的? 这就需要证明。
首先需要证明该排序策略具有传递性,目的是为了证明在该种排序策略下得到的字符串是唯一的。
传递性也就是要证明:根据 (1)
a.b <= b.a;(2)b.c <= c.b,能否推导出a.c <= c.a?例如字符串
“123”和“456”,拼接后为“123456”,如果是K进制,那么这个拼接过程用数学符号代替就是 “123” × \times × K3 + “456”。所有的字符串都可以看做数字,则一定是非负整数。
进一步抽象化,假设所有字符串都是
K进制的,那么 a.b = a × \times × Kb长度 + b,再进一步替代,m(x) = Kx长度。那么第1)2)条件就可以进行改写:
a.b <= b.a可以改写为:a * m(b) + b <= b * m(a) + a①b.c <= c.b可以改写为:b * m(c) + c <= c * m(b) + b②①式左右两边同时减b,再同时乘c:
a * m(b) *c <= c * b * m(a) + a * c - b * c②式左右两边同时减b,再同时乘a:
a * b * m(c) + a * c - b * a <= c * m(b) * a可得
a * b * m(c) + a * c - b * a <= c * b * m(a) + a * c - b * c同时减(a * c ),得到
a * b * m(c) - b*a <= c * b * m(a) - b * c同时含因子 b,两边同时除以b:
a * m(c) - a <= c * m(a) - c挪动a和c:
a * m(c) +c <= c * m(a) + a变成了a.c <= c.a,证明结束。证明得到的字符串一定是字典序最小的。
先证明任意2个字符串如果交换顺序,字典序只会变大。然后任意3个,任意4个,任意5个,…
现在需要证明 [… a m 1 m_1 m1 m 2 m_2 m2 b …] <= [… b m 1 m_1 m1 m 2 m_2 m2 a …]
首先,如果仅仅是交换 a 和 m 1 m_1 m1,那么根据传递性,a. m 1 m_1 m1 <= m 1 m1 m1.a,所以[… a m 1 m_1 m1 m 2 m_2 m2 b …] <= [… m 1 m_1 m1 a m 2 m_2 m2 b …]
同理,又因为 a. m 2 m_2 m2 <= m 2 m_2 m2.a,所以 [… m 1 m_1 m1 a m 2 m_2 m2 b …] <= [… m 1 m_1 m1 m 2 m_2 m2 a b …]
同理,交换a和b,[… m 1 m_1 m1 m 2 m_2 m2 a b …] <= [… m 1 m_1 m1 m 2 m_2 m2 b a …]
再将b 和 m 2 m_2 m2 交换,[… m 1 m_1 m1 m 2 m_2 m2 b a …] <= [… m 1 m_1 m1 b m 2 m_2 m2 a …]
最后,交换b 和 m 1 m_1 m1,所以得到 [… m 1 m_1 m1 b m 2 m_2 m2 a …] <= […b m 1 m_1 m1 m 2 m_2 m2 a …]
总结来说就是: [… a m 1 m_1 m1 m 2 m_2 m2 b …] <= [… b m 1 m_1 m1 m 2 m_2 m2 a …]。
所以交换a和b的顺序,字典序只会增大,不会变小。再去证明交换任意3个、4个、…等等,用数学归纳法推导出来。
但是,实际上贪心策略不需要证明也能知道其正确性。方法论:用暴力解+ 对数器 的方法验证贪心策略的正确性。
3、代码实现
C++ 版
/************************************************************************* > File Name: 034.拼接字符串字典序最小.cpp > Author: Maureen > Mail: [email protected] > Created Time: 四 6/30 21:19:41 2022 ************************************************************************/
#include <iostream>
#include <vector>
#include <set>
#include <cstring>
#include <ctime>
using namespace std;
vector<string> removeIndexString(vector<string> &strs, int index) {
int n = strs.size();
vector<string> ans(n - 1);
int ansIndex = 0;
for (int i = 0; i < n; i++) {
if (i != index) {
ans[ansIndex++] = strs[i];
}
}
return ans;
}
set<string> process(vector<string> &strs) {
set<string> ans;
if (strs.size() == 0) {
ans.insert("");
return ans;
}
for (int i = 0; i < strs.size(); i++) {
string first = strs[i];
vector<string> nexts = removeIndexString(strs, i);
set<string> next = process(nexts);
for (string cur : next) {
ans.insert(first + cur);
}
}
return ans;
}
string lowestString1(vector<string> &strs) {
if (strs.size() == 0) return "";
set<string> ans = process(strs);
auto it = ans.begin();
return ans.size() == 0 ? "" : *it;
}
//方法二:
bool comp(const string &a, const string &b) {
return a + b < b + a;
}
string lowestString2(vector<string> &strs) {
if (strs.size() == 0) return "";
sort(strs.begin(), strs.end(), comp);
string res = "";
for (int i = 0; i < strs.size(); i++) {
res += strs[i];
}
return res;
}
//for test
string generateRandomString(int len) {
char *ans = new char[rand() % len + 1];
for (int i = 0; i < strlen(ans); i++) {
int value = rand() % 5;
ans[i] = (rand() % 100 / (double)101) <= 0.5 ? (char)(65 + value) : (char)(97 + value);
}
return ans;
}
vector<string> generateRandomStringArray(int alen, int slen) {
vector<string> ans(rand() % alen + 1);
for (int i = 0; i < ans.size(); i++) {
ans[i] = generateRandomString(slen);
}
return ans;
}
vector<string> copyStringArray(vector<string> &arr) {
vector<string> ans(arr.size());
for (int i = 0; i < ans.size(); i++) {
ans[i] = arr[i];
}
return ans;
}
int main() {
srand(time(0));
int arrLen = 6;
int strLen = 5;
int testTime = 10001;
cout << "test begin" << endl;
for (int i = 0; i < testTime; i++) {
vector<string> arr1 = generateRandomStringArray(arrLen, strLen);
vector<string> arr2 = copyStringArray(arr1);
if (lowestString1(arr1) != lowestString2(arr2)) {
for (string str : arr1) {
cout << str << " , ";
}
cout << endl;
cout << "Oops!" << endl;
break;
}
if (i && i % 100 == 0) cout << i << " cases passed!" << endl;
}
cout << "finish!" << endl;
return 0;
}
Java 版
package class13;
import java.util.Arrays;
import java.util.Comparator;
import java.util.TreeSet;
public class Code05_LowestLexicography {
//方法一:暴力解
public static String lowestString1(String[] strs) {
if (strs == null || strs.length == 0) {
return "";
}
TreeSet<String> ans = process(strs);
return ans.size() == 0 ? "" : ans.first();
}
// strs中所有字符串全排列,返回所有可能的结果
public static TreeSet<String> process(String[] strs) {
TreeSet<String> ans = new TreeSet<>();
if (strs.length == 0) {
ans.add("");
return ans;
}
for (int i = 0; i < strs.length; i++) {
String first = strs[i];
String[] nexts = removeIndexString(strs, i);
TreeSet<String> next = process(nexts);
for (String cur : next) {
ans.add(first + cur);
}
}
return ans;
}
// {"abc", "cks", "bct"}
// 0 1 2
// removeIndexString(arr , 1) -> {"abc", "bct"}
public static String[] removeIndexString(String[] arr, int index) {
int N = arr.length;
String[] ans = new String[N - 1];
int ansIndex = 0;
for (int i = 0; i < N; i++) {
if (i != index) {
ans[ansIndex++] = arr[i];
}
}
return ans;
}
//方法二:使用对数器
public static class MyComparator implements Comparator<String> {
@Override
public int compare(String a, String b) {
return (a + b).compareTo(b + a);
}
}
public static String lowestString2(String[] strs) {
if (strs == null || strs.length == 0) {
return "";
}
Arrays.sort(strs, new MyComparator());
String res = "";
for (int i = 0; i < strs.length; i++) {
res += strs[i];
}
return res;
}
// for test
public static String generateRandomString(int strLen) {
char[] ans = new char[(int) (Math.random() * strLen) + 1];
for (int i = 0; i < ans.length; i++) {
int value = (int) (Math.random() * 5);
ans[i] = (Math.random() <= 0.5) ? (char) (65 + value) : (char) (97 + value);
}
return String.valueOf(ans);
}
// for test
public static String[] generateRandomStringArray(int arrLen, int strLen) {
String[] ans = new String[(int) (Math.random() * arrLen) + 1];
for (int i = 0; i < ans.length; i++) {
ans[i] = generateRandomString(strLen);
}
return ans;
}
// for test
public static String[] copyStringArray(String[] arr) {
String[] ans = new String[arr.length];
for (int i = 0; i < ans.length; i++) {
ans[i] = String.valueOf(arr[i]);
}
return ans;
}
public static void main(String[] args) {
int arrLen = 6;
int strLen = 5;
int testTimes = 10000;
System.out.println("test begin");
for (int i = 0; i < testTimes; i++) {
String[] arr1 = generateRandomStringArray(arrLen, strLen);
String[] arr2 = copyStringArray(arr1);
if (!lowestString1(arr1).equals(lowestString2(arr2))) {
for (String str : arr1) {
System.out.print(str + ",");
}
System.out.println();
System.out.println("Oops!");
}
}
System.out.println("finish!");
}
}
边栏推荐
- 接口测试框架中的鉴权处理
- FPN网络详解
- sql刷题1050. 合作过至少三次的演员和导演
- Advantages, values and risks of chain games compared with traditional games
- Template Engine Velocity Foundation
- vim用户自动命令示例
- Activity的生命周期和启动模式详解
- 数据库系统原理与应用教程(005)—— yum 离线安装 MySQL5.7(Linux 环境)
- 复杂度相关OJ题(LeetCode、C语言、复杂度、消失的数字、旋转数组)
- StoneDB 为国产数据库添砖加瓦,基于 MySQL 的一体化实时 HTAP 数据库正式开源!
猜你喜欢

sql刷题1050. 合作过至少三次的演员和导演

C语言输入/输出流和文件操作

Building blocks for domestic databases, stonedb integrated real-time HTAP database is officially open source!

Hi Fun Summer, play SQL planner with starrocks!

Stonedb is building blocks for domestic databases, and the integrated real-time HTAP database based on MySQL is officially open source!

游戏行业安全选择游戏盾,效果怎么样?

sql刷题586. 订单最多的客户

【PyG】文档总结以及项目经验(持续更新

PR basic clip operation / video export operation

Submission lottery - light application server essay solicitation activity (may) award announcement
随机推荐
sql刷题1050. 合作过至少三次的演员和导演
Motion capture system for apple picking robot
Zabbix2.2监控之系统及应用日志监控报警
【flask入门系列】Cookie与Session
Template Engine Velocity Foundation
Authentication processing in interface testing framework
P2893 [usaco08feb] making the grade g (DP & priority queue)
全面看待企业数字化转型的价值
StoneDB 为国产数据库添砖加瓦,基于 MySQL 的一体化实时 HTAP 数据库正式开源!
Is the securities account given by the head teacher of goucai school safe? Can I open an account?
Leetcode 77 combination -- backtracking method
免费抽奖 | 《阿巴豆》探索未来系列盲盒数字版权作品全网首发!
P2592 [zjoi2008] birthday party (DP)
Principes et applications du système de base de données (006) - - compilation et installation de MySQL 5.7 (environnement Linux)
Flux d'entrées / sorties et opérations de fichiers en langage C
判断链表是否是回文链表
Research and investment strategy report of neutral protease industry in China (2022 Edition)
Leetcode 216 combined summation III -- backtracking method
VMware virtual machine failed during startup: VMware Workstation is incompatible with hyper-v
Are you still using charged document management tools? I have a better choice! Completely free