问题:编写一个函数来查找字符串数组中最长的公共前缀字符串。
这是一个来自leetcode的简单问题,下面是我的回答VS.解决方案答案。问题是:我的答案胜过运行速度的1.17%,解决方案胜率为79.65%。 为什么我的代码这么慢?
在我们开始操作初始公共字符串之前,我们的代码非常相似。解决方案是通过在String类中调用indexof和substring函数来实现的,并且我使用findCommon函数来完成它,这是由我定义的。
解决方案:
public String longestCommonPrefix(String[] strs) {
if(strs == null || strs.length == 0) return "";
String pre = strs[0];
int i = 1;
while(i < strs.length){
while(strs[i].indexOf(pre) != 0)
pre = pre.substring(0,pre.length()-1);
i++;
}
return pre;
}
这是我的:
public static String longestCommonPrefix(String[] strs){
if(strs == null || strs.length == 0)
return "";
String result = strs[0];
for(int index = 1; index < strs.length; index++)
result = findCommon(result, strs[index]);
return result;
}
public static String findCommon(String a, String b){
String common = "";
for(int index = 0; index < Math.min(a.length(), b.length()); index++)
{
if(a.charAt(index) == b.charAt(index))
common += a.charAt(index);
else
break;
}
return common;
}
在我看来,解决方案代码看起来更简单,因为函数是在String库中定义的。但这并不意味着它们不存在。
答案 0 :(得分:1)
看看你如何构建前缀字符串:
String common = "";
for(int index = 0; index < Math.min(a.length(), b.length()); index++)
{
if(a.charAt(index) == b.charAt(index))
common += a.charAt(index);
else
break;
}
return common;
每次执行
common += a.charAt(index);
Java必须创建一个全新的String
对象,该对象是通过在现有字符串common
的末尾添加新字符而形成的。这意味着制作长度为p的前缀字符串的成本最终为O(p 2 )。如果你有n个字符串,那么程序的运行时就像O(np 2 )。
将此与参考解决方案进行对比:
pre = pre.substring(0,pre.length()-1);
在许多Java实现中,创建子字符串的行为需要时间O(1),因为新字符串可以与原始字符串共享基础字符数组(一些索引被调整以考虑新的起始索引)。这意味着通过p前缀的工作成本将是O(p)而不是O(p 2 ),这可能导致更长字符串的性能大幅提升。
答案 1 :(得分:0)
我用户试图解决这个问题。您可以尝试使用trie
#define MAX 30 //the total number of alphabet is 26, a...z
struct DicTrie{
bool isTerminal;//是否是单词结束标志
int count; //当前字符串出现次数
int branchCount; //计数当前节点的孩子数
struct DicTrie *next[MAX ]; //每个节点 最多 有 MAX 个孩子节点 结构体嵌套
};
int insertTrie(struct DicTrie *root ,char *targetString)
{
if (!targetString) {
return 0;
}
int len = strlen(targetString);
if (len <= 0) {
return 0;
}
struct DicTrie *head = root;
for (int i = 0; i < len; i ++) {
int res = (int)(targetString[i] - 'a');//当前小写字母对应数字
if (head->next[res] == NULL) { //如果是空节点
head->next[res] = (struct DicTrie *)malloc(sizeof(struct DicTrie));//new DicTrie;//则插入新节点元素
head = head->next[res]; //更新头指针 并初始化
head->count = 0; //
for (int j = 0; j < MAX; j ++) {
head->next[j] = NULL;
head->isTerminal = false;
}
head->branchCount = 1;//一个分支
} else {
head = head->next[res];
head->branchCount ++;//分支累计
}
}
head->count ++;//每次插入一个,响应计数都增加1
head->isTerminal = true;
return head->count;
}
char* longestCommonPrefix(char** strs, int strsSize) {
int len = strsSize;
//边界处理
if (len == 0) {
return "";
}
if (len == 1) {
return strs[0];
}
//组织字典树
struct DicTrie *root = NULL;
root = (struct DicTrie *)malloc(sizeof(struct DicTrie));
root->count = 0;
root->branchCount = 0;
for (int i = 0; i < MAX; i ++) {
root->next[i] = NULL; // 空节点
root->isTerminal = false; //
}
//
for (int i = 0;i < len; i ++) {
insertTrie(root, strs[i]);
}
//
int preIndex = 0;
struct DicTrie *head = root;
bool isFlag = false;
int i = 0;
int count = strlen(strs[0]);//任意一字符串都可以 从strs[0]中查即可
for (preIndex = 0; preIndex< count; preIndex ++) {
int targetIndex = strs[0][preIndex] - 'a';
head = head->next[targetIndex];
if (head->branchCount == len) {
i ++;//拿到合法前缀的计数
isFlag = true;
}
}
if (isFlag) {
preIndex = i;
} else {
preIndex = 0;
}
strs[0][preIndex] = '\0';
return strs[0];
}