本文涉及知识点
字典树 哈希表 字符串
LeetCode 100251. 数组中的最短非公共子字符串
给你一个数组 arr ,数组中有 n 个 非空 字符串。
请你求出一个长度为 n 的字符串 answer ,满足:
answer[i] 是 arr[i] 最短 的子字符串,且它不是 arr 中其他任何字符串的子字符串。如果有多个这样的子字符串存在,answer[i] 应该是它们中字典序最小的一个。如果不存在这样的子字符串,answer[i] 为空字符串。
请你返回数组 answer 。
示例 1:
输入:arr = [“cab”,“ad”,“bad”,“c”]
输出:[“ab”,“”,“ba”,“”]
解释:求解过程如下:
- 对于字符串 “cab” ,最短没有在其他字符串中出现过的子字符串是 “ca” 或者 “ab” ,我们选择字典序更小的子字符串,也就是 “ab” 。
- 对于字符串 “ad” ,不存在没有在其他字符串中出现过的子字符串。
- 对于字符串 “bad” ,最短没有在其他字符串中出现过的子字符串是 “ba” 。
- 对于字符串 “c” ,不存在没有在其他字符串中出现过的子字符串。
示例 2:
输入:arr = [“abc”,“bcd”,“abcd”]
输出:[“”,“”,“abcd”]
解释:求解过程如下: - 对于字符串 “abc” ,不存在没有在其他字符串中出现过的子字符串。
- 对于字符串 “bcd” ,不存在没有在其他字符串中出现过的子字符串。
- 对于字符串 “abcd” ,最短没有在其他字符串中出现过的子字符串是 “abcd” 。
提示:
n == arr.length
2 <= n <= 100
1 <= arr[i].length <= 20
arr[i] 只包含小写英文字母。
字典树
用字典树,给个子串编码。记录各编码在那些字符串出现过。如果在两个或更多字符串中出现过,则不是答案。
用 哈希映射更简单,但性能略差。
代码
template<class TData = char, int iTypeNum = 26, TData cBegin = 'a'>
class CTrieNode
{
public:
CTrieNode* AddChar(TData ele, int& iMaxID)
{
#ifdef _DEBUG
if ((ele < cBegin) || (ele >= cBegin + iTypeNum))
{
return nullptr;
}
#endif
const int index = ele - cBegin;
auto ptr = m_vPChilds[ele - cBegin];
if (!ptr)
{
m_vPChilds[index] = new CTrieNode();
#ifdef _DEBUG
m_vPChilds[index]->m_iID = ++iMaxID;
m_childForDebug[ele] = m_vPChilds[index];
#endif
}
return m_vPChilds[index];
}
CTrieNode* GetChild(TData ele)const
{
#ifdef _DEBUG
if ((ele < cBegin) || (ele >= cBegin + iTypeNum))
{
return nullptr;
}
#endif
return m_vPChilds[ele - cBegin];
}
protected:
#ifdef _DEBUG
int m_iID = -1;
std::unordered_map<TData, CTrieNode*> m_childForDebug;
#endif
public:
int m_iLeafIndex = -1;
protected:
CTrieNode* m_vPChilds[iTypeNum] = {
nullptr };
};
template<class TData = char, int iTypeNum = 26, TData cBegin = 'a'>
class CTrie
{
public:
int GetLeadCount()
{
return m_iLeafCount;
}
CTrieNode<TData, iTypeNum, cBegin>* AddA(CTrieNode<TData, iTypeNum, cBegin>* par,TData curValue)
{
auto curNode =par->AddChar(curValue, m_iMaxID);
FreshLeafIndex(curNode);
return curNode;
}
template<class IT>
int Add(IT begin, IT end)
{
auto pNode = &m_root;
for (; begin != end; ++begin)
{
pNode = pNode->AddChar(*begin, m_iMaxID);
}
FreshLeafIndex(pNode);
return pNode->m_iLeafIndex;
}
template<class IT>
CTrieNode<TData, iTypeNum, cBegin>* Search(IT begin, IT end)
{
auto ptr = &m_root;
for (; begin != end; ++begin)
{
ptr = ptr->GetChild(begin);
if (nullptr == ptr)
{
return nullptr;
}
}
return ptr;
}
CTrieNode<TData, iTypeNum, cBegin> m_root;
protected:
void FreshLeafIndex(CTrieNode<TData, iTypeNum, cBegin>* pNode)
{
if (-1 == pNode->m_iLeafIndex)
{
pNode->m_iLeafIndex = m_iLeafCount++;
}
}
int m_iMaxID = 0;
int m_iLeafCount = 0;
};
class Solution {
public:
vector<string> shortestSubstrings(vector<string>& arr) {
CTrie<> trie;
for (const auto& s : arr)
{
const int n = s.size();
m_vSubIndex.emplace_back(n + 1, vector<int>(n, -1));
for (int left = 0; left < n; left++)
{
auto ptr = &trie.m_root;
for (int len = 0; left + len < n; len++)
{
ptr = trie.AddA(ptr,s[left+len]);
m_vSubIndex.back()[len + 1][left] = ptr->m_iLeafIndex;
m_mIndexCount[ptr->m_iLeafIndex].emplace(m_vSubIndex.size());
}
}
}
vector<string> vRet;
for (int i = 0; i < arr.size(); i++)
{
vRet.emplace_back(Cal(arr[i], i));
}
return vRet;
}
string Cal(const string& s, int index)
{
auto& v = m_vSubIndex[index];
const int n = s.size();
for (int len = 1; len <= n; len++)
{
set<string> sets;
for (int left = 0; left < n; left++)
{
const int index = v[len][left];
if (1 == m_mIndexCount[index].size())
{
sets.insert(s.substr(left, len));
if (sets.size() > 1)
{
sets.erase(std::prev(sets.end()));
}
}
}
if (sets.size())
{
return *sets.begin();
}
}
return "";
}
vector<vector<vector<int>>> m_vSubIndex;
unordered_map<int, unordered_set<int>> m_mIndexCount;
int m_r, m_c;
};
测试用例
template<class T, class T2>
void Assert(const T& t1, const T2& t2)
{
assert(t1 == t2);
}
template<class T>
void Assert(const vector<T>& v1, const vector<T>& v2)
{
if (v1.size() != v2.size())
{
assert(false);
return;
}
for (int i = 0; i < v1.size(); i++)
{
Assert(v1[i], v2[i]);
}
}
int main()
{
vector<string> arr;
{
Solution sln;
arr = {
"vbb", "grg", "lexn", "oklqe", "yxav" };
auto res = sln.shortestSubstrings(arr);
Assert({
"b","g","n","k","a" }, res);
}
{
Solution sln;
arr = {
"cab", "ad", "bad", "c" };
auto res = sln.shortestSubstrings(arr);
Assert({
"ab","","ba","" }, res);
}
{
Solution sln;
arr = {
"abc", "bcd", "abcd" };
auto res = sln.shortestSubstrings(arr);
Assert({
"","","abcd" }, res);
}
}
旧版代码
template
class CTrieNode
{
public:
CTrieNode* AddChar(TData ele, int& iMaxID)
{
#ifdef _DEBUG
if ((ele < cBegin) || (ele >= cBegin + iTypeNum))
{
return nullptr;
}
#endif
const int index = ele - cBegin;
auto ptr = m_vPChilds[ele - cBegin];
if (!ptr)
{
m_vPChilds[index] = new CTrieNode();
#ifdef _DEBUG
m_vPChilds[index]->m_iID = ++iMaxID;
m_childForDebug[ele] = m_vPChilds[index];
#endif
}
return m_vPChilds[index];
}
CTrieNode* GetChild(TData ele)const
{
#ifdef _DEBUG
if ((ele < cBegin) || (ele >= cBegin + iTypeNum))
{
return nullptr;
}
#endif
return m_vPChilds[ele - cBegin];
}
protected:
#ifdef _DEBUG
int m_iID = -1;
std::unordered_map<TData, CTrieNode*> m_childForDebug;
#endif
public:
int m_iLeafIndex = -1;
protected:
CTrieNode* m_vPChilds[iTypeNum] = { nullptr };
};
template
class CTrie
{
public:
int GetLeadCount()
{
return m_iLeafCount;
}
template
int Add(IT begin, IT end)
{
auto pNode = &m_root;
for (; begin != end; ++begin)
{
pNode = pNode->AddChar(begin, m_iMaxID);
}
if (-1 == pNode->m_iLeafIndex)
{
pNode->m_iLeafIndex = m_iLeafCount++;
}
return pNode->m_iLeafIndex;
}
template
CTrieNode<TData, iTypeNum, cBegin> Search(IT begin, IT end)
{
auto ptr = &m_root;
for (; begin != end; ++begin)
{
ptr = ptr->GetChild(begin);
if (nullptr == ptr)
{
return nullptr;
}
}
return ptr;
}
CTrieNode<TData, iTypeNum, cBegin> m_root;
protected:
int m_iMaxID = 0;
int m_iLeafCount = 0;
};
class Solution {
public:
vector shortestSubstrings(vector& arr) {
CTrie<> trie;
for (const auto& s : arr)
{
const int n = s.size();
m_vSubIndex.emplace_back(n+1, vector(n, -1));
for (int left = 0; left < n; left++)
{
for (int len = 0; left+len < n; len++)
{
const int index = trie.Add(s.data() + left, s.data() + left + len + 1);
m_vSubIndex.back()[len + 1][left] = index;
m_mIndexCount[index].emplace(m_vSubIndex.size());
}
}
}
vector vRet;
for (int i = 0 ; i < arr.size();i++)
{
vRet.emplace_back(Cal(arr[i], i));
}
return vRet;
}
string Cal(const string& s ,int index)
{
auto& v = m_vSubIndex[index];
const int n = s.size();
for (int len = 1; len <= n ;len++)
{
set sets;
for (int left = 0; left < n; left++)
{
const int index = v[len][left];
if (1 == m_mIndexCount[index].size())
{
sets.insert(s.substr(left, len));
if (sets.size() > 1)
{
sets.erase(std::prev(sets.end()));
}
}
}
if (sets.size())
{
return *sets.begin();
}
}
return “”;
}
vector<vector<vector>> m_vSubIndex;
unordered_map<int, unordered_set> m_mIndexCount;
int m_r, m_c;
};
扩展阅读
视频课程
有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771
如何你想快速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176
相关下载
想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版
https://download.csdn.net/download/he_zhidan/88348653
我想对大家说的话 |
---|
闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。 |
子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。 |
如果程序是一条龙,那算法就是他的是睛 |
测试环境
操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法用**C++**实现。