【字典树(前缀树) 字符串】2416. 字符串的前缀分数和

本文涉及知识点

字典树(前缀树) 字符串

LeetCode 2416. 字符串的前缀分数和

给你一个长度为 n 的数组 words ,该数组由 非空 字符串组成。
定义字符串 word 的 分数 等于以 word 作为 前缀 的 words[i] 的数目。
例如,如果 words = [“a”, “ab”, “abc”, “cab”] ,那么 “ab” 的分数是 2 ,因为 “ab” 是 “ab” 和 “abc” 的一个前缀。
返回一个长度为 n 的数组 answer ,其中 answer[i] 是 words[i] 的每个非空前缀的分数 总和 。
注意:字符串视作它自身的一个前缀。

示例 1:
输入:words = [“abc”,“ab”,“bc”,“b”]
输出:[5,4,3,2]
解释:对应每个字符串的答案如下:

  • “abc” 有 3 个前缀:“a”、“ab” 和 “abc” 。
  • 2 个字符串的前缀为 “a” ,2 个字符串的前缀为 “ab” ,1 个字符串的前缀为 “abc” 。
    总计 answer[0] = 2 + 2 + 1 = 5 。
  • “ab” 有 2 个前缀:“a” 和 “ab” 。
  • 2 个字符串的前缀为 “a” ,2 个字符串的前缀为 “ab” 。
    总计 answer[1] = 2 + 2 = 4 。
  • “bc” 有 2 个前缀:“b” 和 “bc” 。
  • 2 个字符串的前缀为 “b” ,1 个字符串的前缀为 “bc” 。
    总计 answer[2] = 2 + 1 = 3 。
  • “b” 有 1 个前缀:“b”。
  • 2 个字符串的前缀为 “b” 。
    总计 answer[3] = 2 。
    示例 2:

输入:words = [“abcd”]
输出:[4]
解释:
“abcd” 有 4 个前缀 “a”、“ab”、“abc” 和 “abcd”。
每个前缀的分数都是 1 ,总计 answer[0] = 1 + 1 + 1 + 1 = 4 。

提示:

1 <= words.length <= 1000
1 <= words[i].length <= 1000
words[i] 由小写英文字母组成

字典树

字典树各节点记录子树数量。
针对每个word ,一次查询word[0…j] 子孙的数量,累加就可以了。

代码

核心代码

namespace NTmp {
	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
			}
			m_dataToChilds[index]->m_iChildChildCount++;
			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;
		int m_iChildChildCount = 0;
	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:
	vector<int> sumPrefixScores(vector<string>& words) {
		NTmp::CTrie<> trie;
		for (const auto& s: words) {
			trie.Add(s.begin(), s.end());
		}
		vector<int> vRet;
		for (const auto& s : words) {
			auto ptr = &trie.m_root;
			int cnt = 0;
			for (const auto& ch : s) {
				ptr = ptr->GetChild(ch);
				cnt += ptr->m_iChildChildCount;
			}
			vRet.emplace_back(cnt);
		}
		return vRet;
	}
};

测试用例

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]);
	}
}

template<class T>
void Assert(const T& t1, const T& t2)
{
	assert(t1 == t2);
}

int main()
{
	vector<string> words;
	{
		Solution slu;
		words = { "abc", "ab", "bc", "b" };
		auto res = slu.sumPrefixScores(words);
		Assert({ 5,4,3,2 }, res);
	}
	{
		Solution slu;
		words = {"abcd" };
		auto res = slu.sumPrefixScores(words);
		Assert({ 4 }, res);
	}
}

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步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++**实现。

  • 75
    点赞
  • 70
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 35
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 35
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

闻缺陷则喜何志丹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值