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

慈云数据 1年前 (2024-03-15) 技术支持 74 0

涉及知识点

动态规划汇总

多源最短路径 字典树

作者推荐

视频算法专题

题目

给你两个下标从 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

在两次操作中选择的子串分别是 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 assert(t1 == t2); } template if (v1.size() != v2.size()) { assert(false); return; } for (int i = 0; i = 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 class CStrTrieHelp { public: int Add(const string& s) { return m_trie.Add(s.begin(), s.begin() + s.length()); } CTrie* Search(const string& s) { return m_trie.Search(s.begin(), s.begin() + s.length()); } CTrie* SearchSub(const string& s,int iPos,int len) { return m_trie.Search(s.begin()+ iPos, s.begin() + iPos + len ); } CTrie m_trie; }; template 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 m_trie; }; //多源码路径 template class CFloyd { public: CFloyd(const vector& mat) { m_vMat = mat; const int n = mat.size(); for (int i = 0; i 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 CFloyd

    {

    public:

    CFloyd(const vector& mat)

    {

    m_vMat = mat;

    const int n = mat.size();

    for (int i = 0; i

    {//通过i中转

    for (int i1 = 0; i1

    {

    for (int i2 = 0; 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 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 mStrToNode;

    for (int i = 0; i

    {

    mStrToNode[strs[i]] = i;

    }

    const int iNotMay = 1000 * 1000 * 1000;

    vector vMat(strs.size(), vector(strs.size(), iNotMay));

    vector vOriNode;

    for (int j = 0; 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

    {

    vMat[i][i] = 0;

    }

    CFloyd floyd(vMat);

    vector vRet(source.length() + 1,LLONG_MAX/1000 );

    vRet[0]=0;

    for (int i = 0; i

    {

    if (source[i] == target[i])

    {

    vRet[i + 1] = min(vRet[i+1],vRet[i]);

    //continue; 相等也可以替换

    }

    for (int j = 0; 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 CFloyd

    {

    public:

    CFloyd(const vector& mat)

    {

    m_vMat = mat;

    const int n = mat.size();

    for (int i = 0; i

    {//通过i中转

    for (int i1 = 0; i1

    {

    for (int i2 = 0; 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 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 mStrToNode;

    for (int i = 0; i

    {

    mStrToNode[strs[i]] = i;

    }

    const int iNotMay = 1000 * 1000 * 1000;

    vector vMat(strs.size(), vector(strs.size(), iNotMay));

    for (int j = 0; j

    {

    auto& n = vMat[mStrToNode[original[j]]][mStrToNode[changed[j]]];

    n = min(n,cost[j]);

    }

    for (int i = 0; i

    {

    vMat[i][i] = 0;

    }

    CFloyd floyd(vMat);

    vector vRet(source.length() + 1,LLONG_MAX/1000 );

    vRet[0]=0;

    for (int i = 0; i

    {

    for (int len = 1; len + i = iNotMay)

    {

    continue;

    }

    vRet[i + len] = min(vRet[i + len], vRet[i] + iDist);

    }

    }

    return (vRet.back() >= LLONG_MAX / 1000) ? -1 : vRet.back();

    }

    };

    第四版超时

    template

    class CTrie

    {

    public:

    CTrie()

    {

    }
    template
    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
    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 + 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 + iTypeNum))
    	{
    		return nullptr;
    	}
    	return m_vPChilds[ele - cBegin];
    }
    int m_iDebug=-1;
    

    protected:

    int m_iLeve=-1;

    CTrie* m_vPChilds[iTypeNum] = { nullptr };

    };

    template

    class CStrTrieHelp

    {

    public:

    CTrie* Add(const string& s,int iDebug)

    {

    return m_trie.Add(s.begin(), s.begin() + s.length(), iDebug);

    }

    CTrie* Search(const string& s)

    {

    return m_trie.Search(s.begin(), s.begin() + s.length());

    }

    CTrie* SearchSub(const string& s,int iPos,int len)

    {

    return m_trie.Search(s.begin()+ iPos, s.begin() + iPos + len );

    }

    CTrie m_trie;

    };

    template

    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 m_trie;

    };

    //多源码路径

    template

    class CFloyd

    {

    public:

    CFloyd(const vector& mat)

    {

    m_vMat = mat;

    const int n = mat.size();

    for (int i = 0; i

    {//通过i中转

    for (int i1 = 0; i1

    {

    for (int i2 = 0; 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 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

    {

    strIndexs.Add(strs[i], i);

    }

    const int iNotMay = 1000 * 1000 * 1000;

    vector vMat(strs.size(), vector(strs.size(), iNotMay));

    for (int j = 0; 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

    {

    vMat[i][i] = 0;

    }

    CFloyd floyd(vMat);

    vector vRet(source.length() + 1, LLONG_MAX / 1000);

    vRet[0] = 0;

    for (int i = 0; i

    {

    bool bSame = true;

    for (int len = 1; len + i = iNotMay)

    {

    continue;

    }

    vRet[i + len] = min(vRet[i + len], vRet[i] + iDist);

    }

    }

    return (vRet.back() >= LLONG_MAX / 1000) ? -1 : vRet.back();

    }

    };

    扩展阅读

    视频课程

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

微信扫一扫加客服

微信扫一扫加客服