首页 > 其他分享 >动态规划 多源路径 字典树 LeetCode2977:转换字符串的最小成本

动态规划 多源路径 字典树 LeetCode2977:转换字符串的最小成本

时间:2024-01-02 12:38:23浏览次数:32  
标签:begin const strs int LeetCode2977 source vMat 多源 字典


涉及知识点

动态规划 多源最短路径 字典树

题目

给你两个下标从 0 开始的字符串 source 和 target ,它们的长度均为 n 并且由 小写 英文字母组成。
另给你两个下标从 0 开始的字符串数组 original 和 changed ,以及一个整数数组 cost ,其中 cost[i] 代表将字符串 original[i] 更改为字符串 changed[i] 的成本。
你从字符串 source 开始。在一次操作中,如果 存在 任意 下标 j 满足 cost[j] == z 、original[j] == x 以及 changed[j] == y ,你就可以选择字符串中的 子串 x 并以 z 的成本将其更改为 y 。 你可以执行 任意数量 的操作,但是任两次操作必须满足 以下两个 条件 之一 :
在两次操作中选择的子串分别是 source[a…b] 和 source[c…d] ,满足 b < c 或 d < a 。换句话说,两次操作中选择的下标 不相交 。
在两次操作中选择的子串分别是 source[a…b] 和 source[c…d] ,满足 a == c 且 b == d 。换句话说,两次操作中选择的下标 相同 。
返回将字符串 source 转换为字符串 target 所需的 最小 成本。如果不可能完成转换,则返回 -1 。
注意,可能存在下标 i 、j 使得 original[j] == original[i] 且 changed[j] == changed[i] 。
示例 1:
输入:source = “abcd”, target = “acbe”, original = [“a”,“b”,“c”,“c”,“e”,“d”], changed = [“b”,“c”,“b”,“e”,“b”,“e”], cost = [2,5,5,1,2,20]
输出:28
解释:将 “abcd” 转换为 “acbe”,执行以下操作:

  • 将子串 source[1…1] 从 “b” 改为 “c” ,成本为 5 。
  • 将子串 source[2…2] 从 “c” 改为 “e” ,成本为 1 。
  • 将子串 source[2…2] 从 “e” 改为 “b” ,成本为 2 。
  • 将子串 source[3…3] 从 “d” 改为 “e” ,成本为 20 。
    产生的总成本是 5 + 1 + 2 + 20 = 28 。
    可以证明这是可能的最小成本。
    示例 2:
    输入:source = “abcdefgh”, target = “acdeeghh”, original = [“bcd”,“fgh”,“thh”], changed = [“cde”,“thh”,“ghh”], cost = [1,3,5]
    输出:9
    解释:将 “abcdefgh” 转换为 “acdeeghh”,执行以下操作:
  • 将子串 source[1…3] 从 “bcd” 改为 “cde” ,成本为 1 。
  • 将子串 source[5…7] 从 “fgh” 改为 “thh” ,成本为 3 。可以执行此操作,因为下标 [5,7] 与第一次操作选中的下标不相交。
  • 将子串 source[5…7] 从 “thh” 改为 “ghh” ,成本为 5 。可以执行此操作,因为下标 [5,7] 与第一次操作选中的下标不相交,且与第二次操作选中的下标相同。
    产生的总成本是 1 + 3 + 5 = 9 。
    可以证明这是可能的最小成本。
    示例 3:
    输入:source = “abcdefgh”, target = “addddddd”, original = [“bcd”,“defgh”], changed = [“ddd”,“ddddd”], cost = [100,1578]
    输出:-1
    解释:无法将 “abcdefgh” 转换为 “addddddd” 。
    如果选择子串 source[1…3] 执行第一次操作,以将 “abcdefgh” 改为 “adddefgh” ,你无法选择子串 source[3…7] 执行第二次操作,因为两次操作有一个共用下标 3 。
    如果选择子串 source[3…7] 执行第一次操作,以将 “abcdefgh” 改为 “abcddddd” ,你无法选择子串 source[1…3] 执行第二次操作,因为两次操作有一个共用下标 3 。
    参数范围
    1 <= source.length == target.length <= 1000
    source、target 均由小写英文字母组成
    1 <= cost.length == original.length == changed.length <= 100
    1 <= original[i].length == changed[i].length <= source.length
    original[i]、changed[i] 均由小写英文字母组成
    original[i] != changed[i]
    1 <= cost[i] <= 106

分析

将s按顺序拆分成若干子串,任何字符都在一个子串中,且只在一个字串中。求这些子串全部转成t的最小成本。

动态规划

动态规划之状态表示

dp[i]表示将s[0,i)转化成t[0,i)的最小成本

动态规划之状态转移方程

dp[j]=min(dp[i]+s[i,j)转化成t[i,j)成本), i取值范围[0,j)

动态规划之状态之初始化

dp[0]=0

动态规划之状态之填表顺序

两层循环枚举i,j ,先枚举i,再枚举j。s[i,j)是最后一个子串

动态规划之状态之返回值

dp.back()

字符经过多次转化的最小成本

本质就是最短多源路径。

将字符串转成整数(节点编号)

如果用哈希map,每次是O(n),就超时了。可以自写哈希或用字典树,从查询s[i,j)到s[i+j+1)时间复杂度是O(1)。总时间复杂度是O(n2)。

测试用例

template<class T>
void Assert(const T& t1, const T& 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()
{

	string source, target;
	vector<string> original, changed;
	vector<int> cost;

	{
		Solution sln;
		source = "abcdefgh", target = "acdeeghh", original = { "bcd", "fgh", "thh" }, changed = { "cde", "thh", "ghh" }, cost = { 1, 3, 5 };
		auto res = sln.minimumCost(source, target, original, changed, cost);
		Assert(9LL, res);
	}
	{
		Solution sln;
		source = "abcd", target = "acbe";
		original = { "a", "b", "c", "c", "e", "d" }, changed = { "b", "c", "b", "e", "b", "e" };
		cost = { 2, 5, 5, 1, 2, 20 };
		auto res = sln.minimumCost(source, target, original, changed, cost);
		Assert(28LL, res);
	}
	{
		Solution sln;
		source = "abbaacddcbacbddcbdbddcadbadbbdbaabcdbdbdcaccacaddcabadadaccabbddbbdacaacdbdcaaddcacbcbcaaacaddabcabc";
		target = "abbaacdbcbabcadcbdbddcadbadbbddaabcdbdddcacadbacabcbdbbbbdaabbddbbdaabbcdbdcaaddcacbcbcadadccdcbcbcb";
		original = { "b", "c", "a", "cbd", "adc", "ddb", "dcb", "dbd", "b", "caac", "acdc", "cbbd", "bcdb", "ddbc", "aacadda", "cbadbbd", "aaabcad", "bacdccc", "ddabdaa", "abc", "bbc", "cdd", "ddb", "cacaddcabad", "bdcdccabdcb", "bddbbabdcac", "adacc", "bbdca", "dabad", "cddcba" };
		changed = { "c", "a", "d", "adc", "ddb", "dcb", "dbd", "bca", "d", "acdc", "cbbd", "bcdb", "ddbc", "abbc", "cbadbbd", "aaabcad", "bacdccc", "ddabdaa", "dadccdc", "bbc", "cdd", "ddb", "bcb", "bdcdccabdcb", "bddbbabdcac", "adbacabcbdb", "bbdca", "dabad", "bbbda", "cdbcba" };
		cost = { 63, 87, 77, 94, 100, 73, 99, 16, 89, 94, 76, 43, 76, 84, 83, 38, 96, 87, 34, 98, 33, 53, 58, 90, 61, 51, 92, 76, 91, 70 };
		auto res = sln.minimumCost(source, target, original, changed, cost);
		Assert(2044LL, res);
	}
		
	{
		Solution sln;
		source = "aaaddcaaccbabaaccbabbaadcccadbaacbddbaccabddbdbaaddbbacbddddbbdbccaadcaccacdbcbddbacabadaaccbadbbdbc";
		target = "abaddcabcdbabcbadcaccaadabbadddbcacaaabdabbdcbbdbcbaaabbbcddcbddcbccadacddcbdcbacadbbadbdabcbadbbdac";
		original = { "ddddb", "dccbb", "dadac", "dbdbb", "ddbacabadaac", "bcbccdcadabd", "dacabcdaacca", "dcdadacacbbd", "dcccadbaacbddbacc", "dcdcbccdccdbaaaac", "bbbcccdbcdcadaabc", "bccaadcaccacdb", "bbcabcbcbaddbd", "dbadadaddcddad", "badaddbcddacca", "bc", "da", "cb", "ddbdbaaddbbac", "dbcadcdbabddd", "abdadacbbbcca", "adaaabcabdbcc", "caaccbabaaccbabba", "abaadddbaaccbbacc", "bbddaaadcbccccbac", "cdbdbddaadbbbdbdd", "bcbdaabaddbdcdcaa", "aa", "cb", "dd" };
		changed = { "dccbb", "dadac", "dbdbb", "bcddc", "bcbccdcadabd", "dacabcdaacca", "dcdadacacbbd", "acadbbadbdab", "dcdcbccdccdbaaaac", "bbbcccdbcdcadaabc", "dabbadddbcacaaabd", "bbcabcbcbaddbd", "dbadadaddcddad", "badaddbcddacca", "dcbccadacddcbd", "da", "cb", "ac", "dbcadcdbabddd", "abdadacbbbcca", "adaaabcabdbcc", "bdcbbdbcbaaab", "abaadddbaaccbbacc", "bbddaaadcbccccbac", "cdbdbddaadbbbdbdd", "bcbdaabaddbdcdcaa", "cabcdbabcbadcacca", "cb", "dd", "ba" };
		cost = { 67, 56, 64, 83, 100, 73, 95, 97, 100, 98, 20, 92, 58, 70, 95, 77, 95, 93, 69, 92, 77, 53, 96, 68, 83, 96, 93, 64, 81, 100 };
		auto res = sln.minimumCost(source, target, original, changed, cost);
		Assert(2405LL, res);
	}

	

	

//CConsole::Out(res);
}

代码

我写了N版都超时,单个用例不超时,总用例超时。用题解的代码运行了错误和超时用例,速度比我的速度快了近一半。算了,不继续优化了,许多比赛的技巧是工作的灾难,比如用原生数组代替vector。

第六版超时

template<class TData, TData defData,int iTypeNum = 26, TData cBegin = 'a'>
class CTrie
{
public:
	CTrie() 
	{
		m_iID = s_ID++;
	}
	int GetLeadCount()
	{
		return m_iLeafCount;
	}
	template<class IT>
	int Add(IT begin, IT end)
	{
		int iLeve = 0;
		CTrie* pNode = this;
		for (; begin != end; ++begin)
		{
			pNode = pNode->AddChar(*begin);			
			pNode->m_iLeve = iLeve++;
		}
		if (-1 == pNode->m_iLeafID)
		{
			pNode->m_iLeafID = m_iLeafCount++;
		}
		return pNode->m_iLeafID;
	}
	template<class IT>
	CTrie* Search(IT begin, IT end)
	{
		if (begin == end)
		{
			return this;
		}

		if ('.' == *begin)
		{
			for (auto& ptr : m_vPChilds)
			{
				if (!ptr)
				{
					continue;
				}
				auto pSearch = ptr->Search(begin + 1, end);
				if (pSearch)
				{
					return pSearch;
				}
			}
			return nullptr;
		}

		auto ptr = GetChild(*begin);
		if (nullptr == ptr)
		{
			return nullptr;
		}
		return ptr->Search(begin + 1, end);
	}
	TData m_data = defData;

	CTrie* AddChar(TData ele)
	{
		if ((ele < cBegin) || (ele >= cBegin + iTypeNum))
		{
			return nullptr;
		}
		const int index = ele - cBegin;
		auto ptr = m_vPChilds[index];
		if (!ptr)
		{
			m_vPChilds[index] = new CTrie();
		}
		return m_vPChilds[index];
	}
	CTrie* GetChild(TData ele)const
	{
		if ((ele < cBegin) || (ele >= cBegin + iTypeNum))
		{
			return nullptr;
		}
		return m_vPChilds[ele - cBegin];
	}
protected:
	int m_iID;
public:
	int m_iLeafID=-1;
protected:
	int m_iLeve=-1;
	
	inline static int s_ID = 0;
	 int m_iLeafCount = 0;
	CTrie* m_vPChilds[iTypeNum] = { nullptr };
};

template<char defData='a', int iTypeNum = 26, char cBegin = 'a'>
class CStrTrieHelp 
{
public:
	int Add(const string& s)
	{
		return m_trie.Add(s.begin(), s.begin() + s.length());
	}
	CTrie<char, defData, iTypeNum, cBegin>* Search(const string& s)
	{
		return m_trie.Search(s.begin(), s.begin() + s.length());
	}
	CTrie<char, defData, iTypeNum, cBegin>* SearchSub(const string& s,int iPos,int len)
	{
		return m_trie.Search(s.begin()+ iPos, s.begin() + iPos + len );
	}
	CTrie<char, defData, iTypeNum, cBegin> m_trie;
};
template<char defData = 'a', int iTypeNum = 26, char cBegin = 'a'>
class CStrIndexs
{
public:
	void Add(const string& s)
	{
		m_trie.Add(s);
	}
	int Seach(const string& s)
	{
		auto p = m_trie.Search(s);
		if (nullptr == p)
		{
			return -1;
		}
		return p->m_iLeafID;
	}
	int SearchSub(const string& s, int iPos, int len)
	{
		auto p = m_trie.SearchSub(s, iPos, len);
		if (nullptr == p)
		{
			return -1;
		}
		return p->m_iDebug;
	}

	CStrTrieHelp<defData, iTypeNum, cBegin> m_trie;
};

//多源码路径
template<class T,T INF = 1000*1000*1000>
class CFloyd
{
public:
	CFloyd(const  vector<vector<T>>& mat)
	{
		m_vMat = mat;
		const int n = mat.size();
		for (int i = 0; i < n; i++)
		{//通过i中转
			for (int i1 = 0; i1 < n; i1++)
			{
				for (int i2 = 0; i2 < n; i2++)
				{
					//此时:m_vMat[i1][i2] 表示通过[0,i)中转的最短距离
					m_vMat[i1][i2] = min(m_vMat[i1][i2], m_vMat[i1][i] + m_vMat[i][i2]);
					//m_vMat[i1][i2] 表示通过[0,i]中转的最短距离
				}
			}
		}		
	};
	vector<vector<T>> m_vMat;
};

class Solution {
public:
	long long minimumCost(string source, string target, vector<string>& original, vector<string>& changed, vector<int>& cost) {
		CStrIndexs strIndexs;
		for (const auto& s : original)
		{
			strIndexs.Add(s);
		}
		for (const auto& s : changed)
		{
			strIndexs.Add(s);
		}
		const int iNotMay = 1000 * 1000 * 1000;
		vector<vector<int>> vMat(strIndexs.m_trie.m_trie.GetLeadCount(), vector<int>(strIndexs.m_trie.m_trie.GetLeadCount(), iNotMay));
		for (int j = 0; j < original.size(); j++)
		{
			const int iSrc = strIndexs.Seach(original[j]);
			const int iDest = strIndexs.Seach(changed[j]);			
			auto& n = vMat[iSrc][iDest];
			n = min(n, cost[j]);
		}
		for (int i = 0; i < strIndexs.m_trie.m_trie.GetLeadCount(); i++)
		{
			vMat[i][i] = 0;
		}
		CFloyd floyd(vMat);
		vector<long long> vRet(source.length() + 1, LLONG_MAX / 1000);
		vRet[0] = 0;
		for (int i = 0; i < source.length(); i++)
		{
			bool bSame = true;
			auto pSrc = &strIndexs.m_trie.m_trie;
			auto pDst = &strIndexs.m_trie.m_trie;
			for (int len = 1; len + i <= source.length(); len++)
			{
				const char ch1 = source[len + i - 1];
				const char ch2 = target[len + i - 1];				
				bSame &= (ch1 == ch2);
				if (nullptr != pSrc)
				{
					pSrc = pSrc->GetChild(ch1);
				}
				if (nullptr != pDst)
				{
					pDst = pDst->GetChild(ch2);
				}
				if (bSame)
				{
					vRet[i + len] = min(vRet[i + len], vRet[i]);
					continue;
				}					
				if ((nullptr == pSrc) || (nullptr == pDst))
				{
					break;
				}
				const int iSrc = pSrc->m_iLeafID;
				const int iDest = pDst->m_iLeafID;
				if ((-1 == iSrc) || (-1== iDest))
				{
					continue;
				}
				const int iDist = floyd.m_vMat[iSrc][iDest];
				if (iDist >= iNotMay)
				{
					continue;
				}
				vRet[i + len] = min(vRet[i + len], vRet[i] + iDist);
			}
		}
		return (vRet.back() >= LLONG_MAX / 1000) ? -1 : vRet.back();
	}
};

第一版超时

//多源码路径
 template<class T,T INF = 100010001000>
 class CFloyd
 {
 public:
 CFloyd(const vector<vector>& mat)
 {
 m_vMat = mat;
 const int n = mat.size();
 for (int i = 0; i < n; i++)
 {//通过i中转
 for (int i1 = 0; i1 < n; i1++)
 {
 for (int i2 = 0; i2 < n; i2++)
 {
 //此时:m_vMat[i1][i2] 表示通过[0,i)中转的最短距离
 m_vMat[i1][i2] = min(m_vMat[i1][i2], m_vMat[i1][i] + m_vMat[i][i2]);
 //m_vMat[i1][i2] 表示通过[0,i]中转的最短距离
 }
 }
 } 
 };
 vector<vector> m_vMat;
 };class Solution {
 public:
 long long minimumCost(string source, string target, vector& original, vector& changed, vector& cost) {
 vector strs(original.begin(), original.end());
 strs.insert(strs.end(), changed.begin(), changed.end());
 sort(strs.begin(),strs.end());
 strs.erase(std::unique(strs.begin(), strs.end()), strs.end());
 std::unordered_map<string, int> mStrToNode;
 for (int i = 0; i < strs.size(); i++)
 {
 mStrToNode[strs[i]] = i;
 }
 const int iNotMay = 1000 * 1000 * 1000;
 vector<vector> vMat(strs.size(), vector(strs.size(), iNotMay));
 vector vOriNode;
 for (int j = 0; j < original.size(); j++)
 {
 vOriNode.emplace_back(mStrToNode[original[j]]);
 auto& n = vMat[vOriNode.back()][mStrToNode[changed[j]]];
 n = min(n,cost[j]);
 }
 for (int i = 0; i < strs.size(); i++)
 {
 vMat[i][i] = 0;
 }
 CFloyd floyd(vMat);
 vector vRet(source.length() + 1,LLONG_MAX/1000 );
 vRet[0]=0; 
 for (int i = 0; i < source.length(); i++)
 {
 if (source[i] == target[i])
 {
 vRet[i + 1] = min(vRet[i+1],vRet[i]);
 //continue; 相等也可以替换
 }
 for (int j = 0; j < original.size(); j++)
 {
 const int len = original[j].length();
 if (i + len > source.length())
 {
 continue;
 }
 if (source.substr(i, len) != original[j])
 {
 continue;
 }
 string sDst = target.substr(i, len);
 if (!mStrToNode.count(sDst))
 {
 continue;
 }
 const int iDest = mStrToNode[sDst];
 const int iDist = floyd.m_vMat[vOriNode[j]][iDest];
 if (iDist >= iNotMay)
 {
 continue;
 }
 vRet[i + len] = min(vRet[i + len],vRet[i]+iDist);
 }
 }
 return (vRet.back() >= LLONG_MAX / 1000) ? -1 : vRet.back();
 }
 };

第二版超时

//多源码路径
 template<class T,T INF = 100010001000>
 class CFloyd
 {
 public:
 CFloyd(const vector<vector>& mat)
 {
 m_vMat = mat;
 const int n = mat.size();
 for (int i = 0; i < n; i++)
 {//通过i中转
 for (int i1 = 0; i1 < n; i1++)
 {
 for (int i2 = 0; i2 < n; i2++)
 {
 //此时:m_vMat[i1][i2] 表示通过[0,i)中转的最短距离
 m_vMat[i1][i2] = min(m_vMat[i1][i2], m_vMat[i1][i] + m_vMat[i][i2]);
 //m_vMat[i1][i2] 表示通过[0,i]中转的最短距离
 }
 }
 } 
 };
 vector<vector> m_vMat;
 };class Solution {
 public:
 long long minimumCost(string source, string target, vector& original, vector& changed, vector& cost) {
 vector strs(original.begin(), original.end());
 strs.insert(strs.end(), changed.begin(), changed.end());
 sort(strs.begin(),strs.end());
 strs.erase(std::unique(strs.begin(), strs.end()), strs.end());
 std::unordered_map<string, int> mStrToNode;
 for (int i = 0; i < strs.size(); i++)
 {
 mStrToNode[strs[i]] = i;
 }
 const int iNotMay = 1000 * 1000 * 1000;
 vector<vector> vMat(strs.size(), vector(strs.size(), iNotMay));
 for (int j = 0; j < original.size(); j++)
 {
 auto& n = vMat[mStrToNode[original[j]]][mStrToNode[changed[j]]];
 n = min(n,cost[j]);
 }
 for (int i = 0; i < strs.size(); i++)
 {
 vMat[i][i] = 0;
 }
 CFloyd floyd(vMat);
 vector vRet(source.length() + 1,LLONG_MAX/1000 );
 vRet[0]=0; 
 for (int i = 0; i < source.length(); i++)
 {
 for (int len = 1; len + i <= source.length(); len++)
 {
 const string sSrc = source.substr(i, len);
 const string sDst = target.substr(i, len);
 if (sSrc == sDst)
 {
 vRet[i + len] = min(vRet[i + len], vRet[i] );
 continue;
 }
 if (!mStrToNode.count(sDst)|| !mStrToNode.count(sSrc))
 {
 continue;
 }
 const int iSrc = mStrToNode[sSrc];
 const int iDest = mStrToNode[sDst];
 const int iDist = floyd.m_vMat[iSrc][iDest];
 if (iDist >= iNotMay)
 {
 continue;
 }
 vRet[i + len] = min(vRet[i + len], vRet[i] + iDist);
 }
 }
 return (vRet.back() >= LLONG_MAX / 1000) ? -1 : vRet.back();
 }
 };

第四版超时

template<class TData, TData defData,int iTypeNum = 26, TData cBegin = ‘a’>
 class CTrie
 {
 public:
 CTrie()
 {}
template<class IT>
CTrie* Add(IT begin, IT end,const int iDebug)
{
	int iLeve = 0;
	CTrie* pNode = this;
	for (; begin != end; ++begin)
	{
		pNode = pNode->AddChar(*begin);			
		pNode->m_iLeve = iLeve++;
	}
	pNode->m_iDebug = iDebug;
	return pNode;
}
template<class IT>
CTrie* Search(IT begin, IT end)
{
	if (begin == end)
	{
		return this;
	}

	if ('.' == *begin)
	{
		for (auto& ptr : m_vPChilds)
		{
			if (!ptr)
			{
				continue;
			}
			auto pSearch = ptr->Search(begin + 1, end);
			if (pSearch)
			{
				return pSearch;
			}
		}
		return nullptr;
	}

	auto ptr = GetChild(*begin);
	if (nullptr == ptr)
	{
		return nullptr;
	}
	return ptr->Search(begin + 1, end);
}
TData m_data = defData;

CTrie* AddChar(TData ele)
{
	if ((ele < cBegin) || (ele >= cBegin + iTypeNum))
	{
		return nullptr;
	}
	const int index = ele - cBegin;
	auto ptr = m_vPChilds[index];
	if (!ptr)
	{
		m_vPChilds[index] = new CTrie();
	}
	return m_vPChilds[index];
}

CTrie* GetChild(TData ele)const
{
	if ((ele < cBegin) || (ele >= cBegin + iTypeNum))
	{
		return nullptr;
	}
	return m_vPChilds[ele - cBegin];
}

int m_iDebug=-1;protected:
 int m_iLeve=-1;
 CTrie* m_vPChilds[iTypeNum] = { nullptr };
 };template<char defData, int iTypeNum = 26, char cBegin = ‘a’>
 class CStrTrieHelp
 {
 public:
 CTrie<char, defData, iTypeNum, cBegin>* Add(const string& s,int iDebug)
 {
 return m_trie.Add(s.begin(), s.begin() + s.length(), iDebug);
 }
 CTrie<char, defData, iTypeNum, cBegin>* Search(const string& s)
 {
 return m_trie.Search(s.begin(), s.begin() + s.length());
 }
 CTrie<char, defData, iTypeNum, cBegin>* SearchSub(const string& s,int iPos,int len)
 {
 return m_trie.Search(s.begin()+ iPos, s.begin() + iPos + len );
 }
 CTrie<char, defData, iTypeNum, cBegin> m_trie;
 };
 template<char defData = ‘a’, int iTypeNum = 26, char cBegin = ‘a’>
 class CStrIndexs
 {
 public:
 void Add(const string& s, int iDebug)
 {
 m_trie.Add(s, iDebug);
 }
 int Seach(const string& s)
 {
 auto p = m_trie.Search(s);
 if (nullptr == p)
 {
 return -1;
 }
 return p->m_iDebug;
 }
 int SearchSub(const string& s, int iPos, int len)
 {
 auto p = m_trie.SearchSub(s, iPos, len);
 if (nullptr == p)
 {
 return -1;
 }
 return p->m_iDebug;
 }
 protected:
 CStrTrieHelp<defData, iTypeNum, cBegin> m_trie;
 };//多源码路径
 template<class T,T INF = 100010001000>
 class CFloyd
 {
 public:
 CFloyd(const vector<vector>& mat)
 {
 m_vMat = mat;
 const int n = mat.size();
 for (int i = 0; i < n; i++)
 {//通过i中转
 for (int i1 = 0; i1 < n; i1++)
 {
 for (int i2 = 0; i2 < n; i2++)
 {
 //此时:m_vMat[i1][i2] 表示通过[0,i)中转的最短距离
 m_vMat[i1][i2] = min(m_vMat[i1][i2], m_vMat[i1][i] + m_vMat[i][i2]);
 //m_vMat[i1][i2] 表示通过[0,i]中转的最短距离
 }
 }
 } 
 };
 vector<vector> m_vMat;
 };class Solution {
 public:
 long long minimumCost(string source, string target, vector& original, vector& changed, vector& cost) {
 vector strs(original.begin(), original.end());
 strs.insert(strs.end(), changed.begin(), changed.end());
 sort(strs.begin(), strs.end());
 strs.erase(std::unique(strs.begin(), strs.end()), strs.end());
 CStrIndexs strIndexs;
 for (int i = 0; i < strs.size(); i++)
 {
 strIndexs.Add(strs[i], i);
 }
 const int iNotMay = 1000 * 1000 * 1000;
 vector<vector> vMat(strs.size(), vector(strs.size(), iNotMay));
 for (int j = 0; j < original.size(); j++)
 {
 const int iSrc = strIndexs.Seach(original[j]);
 const int iDest = strIndexs.Seach(changed[j]); 
 auto& n = vMat[iSrc][iDest];
 n = min(n, cost[j]);
 }
 for (int i = 0; i < strs.size(); i++)
 {
 vMat[i][i] = 0;
 }
 CFloyd floyd(vMat);
 vector vRet(source.length() + 1, LLONG_MAX / 1000);
 vRet[0] = 0;
 for (int i = 0; i < source.length(); i++)
 {
 bool bSame = true;
 for (int len = 1; len + i <= source.length(); len++)
 {
 bSame &= (source[len + i - 1] == target[len + i - 1]);
 if (bSame)
 {
 vRet[i + len] = min(vRet[i + len], vRet[i]);
 continue;
 } 
 const int iSrc = strIndexs.SearchSub(source, i, len);
 const int iDest = strIndexs.SearchSub(target, i, len);
 if ((-1 == iSrc) || (-1== iDest))
 {
 continue;
 }
 const int iDist = floyd.m_vMat[iSrc][iDest];
 if (iDist >= iNotMay)
 {
 continue;
 }
 vRet[i + len] = min(vRet[i + len], vRet[i] + iDist);
 }
 }
 return (vRet.back() >= LLONG_MAX / 1000) ? -1 : vRet.back();
 }
 };



相关下载

想高屋建瓴的学习算法,请下载《喜缺全书算法册》doc版

我想对大家说的话

闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。

子墨子言之:事无终始,无务多业。也就是我们常说的专业的人做专业的事。

如果程序是一条龙,那算法就是他的是睛

测试环境

操作系统:win7 开发环境: VS2019 C++17
或者 操作系统:win10 开发环境: VS2022 C++17
如无特殊说明,本算法C++ 实现。

动态规划 多源路径 字典树 LeetCode2977:转换字符串的最小成本_算法


标签:begin,const,strs,int,LeetCode2977,source,vMat,多源,字典
From: https://blog.51cto.com/u_15724537/9067515

相关文章

  • python json.dump 将字典写文件json文件后多了一个花括号"}",读取时报错:json.decoder.
    问题复现我的字典格式:importjsond={'Demo':{'Total_Result':'Pass','info':{'test3':'Pass'}},'10.0.0.111':{'Total_Result':'Pass','info':{'test......
  • 字典类型的写法与定义
    #定义一个字典,保存一个同学的信息数据a={'姓名':'罗科','英文名':'ROCCO','年龄':32,'身高':170}print(len(a))#获取数据值长度#写法:字典类型:{键:值}#定义:数据中是对应关系时,用字典类型来保存数据'''特点1、字典类型中的数据是有对应关系2、字典类型中的键(冒号前面)是唯一......
  • python字典中删除键值的方法
    一、pop()方法删除keyPython字典是一种无序的映射数据类型,通过键值对的形式进行存储,可以使用键来快速找到对应的值。在某些情况下,我们可能需要在字典中删除某个键,这时候就可以使用Python字典提供的pop()方法。pop()方法用于删除字典中指定的键,并返回该键对应的值。使用该方法时需传......
  • 官答|初始化GreatSQL报错无法找到数据目录或初始化数据字典失败
    官答|初始化GreatSQL报错无法找到数据目录或初始化数据字典失败GreatSQL推出新栏目——官答官答栏目针对GreatSQL数据库中的问题,选取官方论坛和讨论群中的典型提问进行深入解答。内容涵盖数据库安装部署、配置优化、故障排查、性能测试等方面。在文章中,我们不仅提供解决方案,......
  • 洛谷B3647 【模板】Floyd 题解 floyd算法 求 多源多汇最短路
    题目链接:https://www.luogu.com.cn/problem/B3647floyd算法:https://oi-wiki.org/graph/shortest-path/#floyd-算法示例程序:#include<bits/stdc++.h>usingnamespacestd;constintmaxn=101;intn,m,f[maxn][maxn];intmain(){scanf("%d%d",&n......
  • C#深度理解:数组、集合、哈希、字典、堆、栈 优缺点
    一、数组1、Array固定数组优点:1).快速访问:数组通过索引来访问元素,访问速度非常快,因为可以通过索引进行直接定位。2).内存连续存储:数组在内存中以连续的方式存储元素,这样有助于提高数据的读取和写入效率。3).多维支持:C#中的数组支持多维(二维、三维等)数据结构,可以用于表示......
  • Python 把包含\\u4f20\\u5a92 unicode内容的字典字符串变成字典
    importjson#把包含\\u4f20\\u5a92unicode内容的字典字符串变成字典deftext_to_dict(text):dict1=json.loads(text)str_dict=str(dict1).replace('\\xa0','').replace('\'','"')dict_json=json.loads(s......
  • mysql新增字典
    INSERTINTO`xxx`.`sys_dict`(`type`,`description`,`create_time`,`update_time`,`remarks`,`system`,`del_flag`,`tenant_id`)VALUES('status_flag','xxx','2022-08-0113:35:50','2022-08-0113:35:50','xxx&......
  • LANDSAT LC08 C02 T1_L2 metadata dictory 元数据字典
    LANDSAT/LC08/C02/T1_L2metadatadictory元数据字典2023年12月20日星期三byxrkvarimage=ee.ImageCollection('LANDSAT/LC08/C02/T1_L2').first();print("image",image);vardic=image.toDictionary();print("dic",dic)ALGORITHM_SOURCE_SURFACE_REFLE......
  • Python字典中 fromkeys()方法的坑
    fromkeys()方法类似于列表的浅拷贝首先用该方法创建一个字典dict_=dict.fromkeys(('a','b','c','d'),[])print(dict_)输出:{'a':[],'b':[],'c':[],'d':[]}当我向某个key添加value时,问题出现了dict_['b......