本文涉及知识点
字典树 最长公共前缀
LeetCode1316. 不同的循环子字符串
给你一个字符串 text ,请你返回满足下述条件的 不同 非空子字符串的数目:
可以写成某个字符串与其自身相连接的形式(即,可以写为 a + a,其中 a 是某个字符串)。
例如,abcabc 就是 abc 和它自身连接形成的。
示例 1:
输入:text = “abcabcabc”
输出:3
解释:3 个子字符串分别为 “abcabc”,“bcabca” 和 “cabcab” 。
示例 2:
输入:text = “leetcodeleetcode”
输出:2
解释:2 个子字符串为 “ee” 和 “leetcodeleetcode” 。
提示:
1 <= text.length <= 2000
text 只包含小写英文字母。
字典树
令 N = text.length
i,j皆从小到大加到字典树中。
令x 是text[i…j]在字典树中的索引。
则 vIndex[x] 记录所有索引为x的i。
vLen[x] = j - i + 1。
由于i 是从小到大处理,所以 vIndex[x]是升序。
对于
∀
\forall
∀y1
∈
\in
∈ vLen[x] 如果y1+vLen[x]也存在,则子串[y1…y1+vLen[x]*2-1]符合。
只需要枚举 text[i…j] 的长度超过N/2的子串。否则 超出内存限制。
字典树的时间复杂度在超出时间限制的边缘。
时间复杂度: O(NN)。
每个字符被枚举N次,共N个字符,故时间复杂度是O(NN)。
代码
核心代码
template<class TData = char, int iTypeNum = 26, TData cBegin = 'a'>
class CTrieNode
{
public:
~CTrieNode()
{
for (auto& [tmp, ptr] : m_dataToChilds) {
delete ptr;
}
}
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_dataToChilds[ele - cBegin];
if (!ptr)
{
m_dataToChilds[index] = new CTrieNode();
#ifdef _DEBUG
m_dataToChilds[index]->m_iID = ++iMaxID;
m_childForDebug[ele] = m_dataToChilds[index];
#endif
}
return m_dataToChilds[index];
}
CTrieNode* GetChild(TData ele)
{
#ifdef _DEBUG
if ((ele < cBegin) || (ele >= cBegin + iTypeNum))
{
return nullptr;
}
#endif
return m_dataToChilds[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_dataToChilds[iTypeNum] = { nullptr };//空间换时间 大约216字节
//unordered_map<int, CTrieNode*> m_dataToChilds;//时间换空间 大约56字节
map<int, CTrieNode*> m_dataToChilds;//时间换空间,空间略优于哈希映射,数量小于256时,时间也优。大约48字节
};
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:
int distinctEchoSubstrings(string text) {
CTrie<> tree;
vector<vector<int>> vIndexs;
vector<int> vLen;
const int N = text.length();
for (int i = 0; i < N; i++) {
auto ptr = &tree.m_root;
for (int j = i; ((j-i+1) <= N/2)&&(j < N) ; j++) {
ptr = tree.AddA(ptr, text[j]);
if (vLen.size() == ptr->m_iLeafIndex) {
vLen.emplace_back(j - i + 1);
vIndexs.emplace_back();
}
vIndexs[ptr->m_iLeafIndex].emplace_back(i);
}
}
int iRet = 0;
for (int i = 0; i < vLen.size(); i++) {
bool bHas = false;
for (const auto& inx : vIndexs[i]) {
auto it = std::lower_bound(vIndexs[i].begin(), vIndexs[i].end(),inx+vLen[i]);
bool cur = (vIndexs[i].end() != it) && (inx + vLen[i] == *it);
bHas |= cur;
}
iRet += bHas;
}
return iRet;
}
};
最长公共前缀
class Solution {
public:
int distinctEchoSubstrings(string text) {
m_c = text.length();
vector<vector<int>> vLen(m_c, vector<int>(m_c));
for (int i = m_c - 1; i >= 0; i--)
{
for (int j = m_c - 1; j >= 0; j--)
{
if (text[i] != text[j])
{
continue;
}
if ((i + 1 < m_c) && (j + 1 < m_c))
{
vLen[i][j] = 1 + vLen[i + 1][j + 1];
}
else
{
vLen[i][j] = 1;
}
}
}
int iRet = 0;
std::unordered_set<string> setHas;
for (int i = 0; i < m_c; i++)
{
for (int len = 1; i + len * 2 <= m_c; len++)
{
if (vLen[i][i + len] >= len)
{
string strSub = text.substr(i, len);
if (setHas.count(strSub))
{
continue;
}
setHas.emplace(strSub);
iRet++;
}
}
}
return iRet;
}
int 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++**实现。