【字典树】 【哈希表】 【字符串】100251. 数组中的最短非公共子字符串

本文涉及知识点

字典树 哈希表 字符串

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++**实现。

猜你喜欢

转载自blog.csdn.net/he_zhidan/article/details/136602149