给定一个字符串列表,任务是通过一次选择任意两个字符串来找到最大长度的所有 LCP(最长公共前缀)的总和。
例子:
Input: str[] = {babab, ababb, abbab, aaaaa, babaa, babbb}
Output: 6
Explanation:
Choose 1st and 5th string => length of LCP = 4,
Choose 2nd and 3rd string => length of LCP = 2
Sum of LCP = 4 + 2 = 6
Input: str = [“aa”, “aaaa”, “aaaaaaaa”, “aaaabaaaa”, “aaabaaa”]
Output: 7
Explanation:
Choose 3rd (aaaaaaaa) and 4th string (aaaabaaaa) => length of LCP (aaaa) = 4,
Choose 2nd (aaaa) and 5th (aaabaaa) string => length of LCP (aaa) = 3
Sum of LCP = 4 + 3 = 7
天真的方法:
- 按长度降序对字符串列表进行排序
- 然后从列表中取出第一个字符串并找到列表中所有其他剩余字符串的最长公共前缀并将其存储在数组中
- 从数组中选择最大值并将其添加到变量 answer 并从与该总和对应的列表中删除字符串对
- 对所有接下来的字符串重复上述过程,直到列表为空或到达最后一个字符串
- 变量 answer 具有最大长度的所有 LCP 的所需总和
时间复杂度: O(M*N 2 ),其中 M = 最大字符串长度,N =字符串。
有效的方法:
使用 Trie 数据结构可以获得有效的解决方案。为了找到字符串之间共有的字符数,我们将使用变量“visited”来跟踪一个字符被访问的次数。
以下是步骤:
- 在 trie 中插入字符串列表,以便将列表中的每个字符串作为单独的 trie 节点插入。
- 对于所有最大长度的前缀,从树中最深的节点开始计算对。
- 使用深度优先搜索 (DFS) 遍历 trie 来计算最深节点的对。
- 如果访问节点的值大于 1,则表示在该节点之前有两个或多个具有公共前缀的字符串。
- 将该访问节点的值添加到变量计数中。
- 从当前节点和先前节点减少该访问节点的值,以便必须删除选择用于计算的词对。
- 对所有节点重复上述步骤并返回count的值。
下面是上述方法的实现:
C++
// C++ program to find Sum of all LCP
// of maximum length by selecting
// any two Strings at a time
#include
using namespace std;
class TrieNode {
public:
char val;
// Using map to store the pointers
// of children nodes for dynamic
// implementation, for making the
// program space efiicient
map children;
// Counts the number of times the node
// is visited while making the trie
int visited;
// Initially visited value for all
// nodes is zero
TrieNode(char x)
{
val = x;
visited = 0;
}
};
class Trie {
public:
TrieNode* head;
// Head node of the trie is initialize
// as '\0', after this all strings add
Trie()
{
head = new TrieNode('\0');
}
// Function to insert the strings in
// the trie
void addWord(string s)
{
TrieNode* temp = head;
const unsigned int n = s.size();
for (int i = 0; i < n; i++) {
// Inserting character-by-character
char ch = s[i];
// If the node of ch is not present in
// map make a new node and add in map
if (!temp->children[ch]) {
temp->children[ch] = new TrieNode(ch);
}
temp = temp->children[ch];
temp->visited++;
}
}
// Recursive function to calculate the
// answer argument is passed by reference
int dfs(TrieNode* node, int& ans, int depth)
{
// To store changed visited values from
// children of this node i.e. number of
// nodes visited by its children
int vis = 0;
for (auto child : node->children) {
vis += dfs(child.second, ans, depth + 1);
}
// Updating the visited variable, telling
// number of nodes that have
// already been visited by its children
node->visited -= vis;
int string_pair = 0;
// If node->visited > 1, means more than
// one string has prefix up till this node
// common in them
if (node->visited > 1) {
// Number of string pair with current
// node common in them
string_pair = (node->visited / 2);
ans += (depth * string_pair);
// Updating visited variable of current node
node->visited -= (2 * string_pair);
}
// Returning the total number of nodes
// already visited that needs to be
// updated to previous node
return (2 * string_pair + vis);
}
// Function to run the dfs function for the
// first time and give the answer variable
int dfshelper()
{
// Stores the final answer
// as sum of all depths
int ans = 0;
dfs(head, ans, 0);
return ans;
}
};
// Driver Function
int main()
{
Trie T;
string str[]
= { "babab", "ababb", "abbab",
"aaaaa", "babaa", "babbb" };
int n = 6;
for (int i = 0; i < n; i++) {
T.addWord(str[i]);
}
int ans = T.dfshelper();
cout << ans << endl;
return 0;
}
Java
// Java program to find Sum of all LCP
// of maximum length by selecting
// any two Strings at a time
import java.util.*;
class GFG
{
static class TrieNode
{
char val;
// Using map to store the pointers
// of children nodes for dynamic
// implementation, for making the
// program space efiicient
HashMap children;
// Counts the number of times the node
// is visited while making the trie
int visited;
// Initially visited value for all
// nodes is zero
TrieNode(char x)
{
val = x;
visited = 0;
children = new HashMap<>();
}
}
static class Trie
{
TrieNode head;
int ans;
// Head node of the trie is initialize
// as '\0', after this all Strings add
Trie()
{
head = new TrieNode('\0');
ans = 0;
}
// Function to insert the Strings in
// the trie
void addWord(String s)
{
TrieNode temp = head;
int n = s.length();
for (int i = 0; i < n; i++)
{
// Inserting character-by-character
char ch = s.charAt(i);
// If the node of ch is not present in
// map make a new node and add in map
if (temp.children.get(ch) == null)
{
temp.children.put(ch, new TrieNode(ch));
}
temp = temp.children.get(ch);
temp.visited++;
}
}
// Recursive function to calculate the
// answer argument is passed by reference
int dfs(TrieNode node, int depth)
{
// To store changed visited values from
// children of this node i.e. number of
// nodes visited by its children
int vis = 0;
Iterator hmIterator = node.children.entrySet().iterator();
while (hmIterator.hasNext())
{
Map.Entry child = (Map.Entry)hmIterator.next();
vis += dfs((TrieNode)child.getValue(), depth + 1);
}
// Updating the visited variable, telling
// number of nodes that have
// already been visited by its children
node.visited -= vis;
int String_pair = 0;
// If node.visited > 1, means more than
// one String has prefix up till this node
// common in them
if (node.visited > 1)
{
// Number of String pair with current
// node common in them
String_pair = (node.visited / 2);
ans += (depth * String_pair);
// Updating visited variable of current node
node.visited -= (2 * String_pair);
}
// Returning the total number of nodes
// already visited that needs to be
// updated to previous node
return (2 * String_pair + vis);
}
// Function to run the dfs function for the
// first time and give the answer variable
int dfshelper()
{
// Stores the final answer
// as sum of all depths
ans = 0;
dfs(head, 0);
return ans;
}
}
// Driver code
public static void main(String args[])
{
Trie T = new Trie();
String str[]
= { "babab", "ababb", "abbab",
"aaaaa", "babaa", "babbb" };
int n = 6;
for (int i = 0; i < n; i++)
{
T.addWord(str[i]);
}
int ans = T.dfshelper();
System.out.println( ans );
}
}
// This code is contributed by Arnab Kundu
6
时间复杂度:
插入所有的字符串: O(MN)
执行特里遍历: O(26*M) ~ O(M)
因此,总体时间复杂度: O(M*N) ,其中:
N = Number of strings
M = Length of the largest string
辅助空间: O(M)
如果您想与行业专家一起参加直播课程,请参阅Geeks Classes Live