【问题标题】:Getting wrong answer in a DP problem although implementation looks correct尽管实现看起来正确,但在 DP 问题中得到错误答案
【发布时间】:2026-02-06 12:05:02
【问题描述】:

我试图在 codechef 上解决 Reduce String 问题 给出一个长度为 l 的字符串 s 和一个包含 n 个样本字符串的集合 S。我们确实通过这种方式使用集合 S 减少字符串 s: 只要 Si 作为字符串 s 的连续子字符串出现,您可以删除(或不删除)它。 每次删除后,将被删除子串的左右部分连接起来,得到一个新的字符串s。

我写了一个递归函数如下:- 基本上我在我的代码中所做的是要么不删除字符,要么如果它是任何子字符串的一部分则将其删除,但它给出了错误的答案。

#include <bits/stdc++.h>
using namespace std;
#define mx 255

int dp[mx];
unordered_map<string,int> sol;

void init(int n)
{
    for(int i=0;i<n;i++)
    {
        dp[i]=-1;
    }
}

int solve(string str,int low,int high,vector<string> smp)
{
     if(low>high)
     {
        return 0;
     }
     if(dp[low]!=-1)
     {
        return dp[low];
     }
     int ans=1+solve(str,low+1,high,smp);
     for(int i=low;i<high;i++)
     {
        string tem=str.substr(low,i-low+1);
        for(int j=0;j<smp.size();j++)
        {
            cout<<"low i high str"<<low<<" "<<i<<" "<<high<<" "<<smp[j]<<" "<<tem<<endl;
            if(tem.compare(smp[j])==0)
            {
                ans=min(ans,solve(str,i+1,high,smp));
            }
        }
     }
     return dp[low]=ans;
}

signed main()
{
    sol.clear();
    string str;
    vector<string> smp;
    int n;
    cin>>str;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        string tem;
        cin>>tem;
        smp.push_back(tem);
    }
    int len=str.length();
    init(len+1);
    cout<<solve(str,0,len-1,smp)<<endl;
    return 0;
}

PS: link to the question

【问题讨论】:

  • 你应该永远 #include &lt;bits/stdc++.h&gt;。它不是正确的 C++。它破坏了便携性并养成了糟糕的习惯。见Why should I not #include &lt;bits/stdc++.h&gt;。另外,请尽量避免使用using namespace std;,因为它是considered bad practice? 您是否尝试过通过调试器运行您的代码?您应该识别有问题的代码并发布重现问题所需的最少代码。请参阅minimal reproducible example 了解更多信息。
  • 为什么你认为对字符串长度的动态规划是有效的?如果您的删除字符串是 'a' 和 'b',并且字符串是 'aaaabb',则有一大堆可能的 4 个长字符串,而这 4 个可能的长字符串在变短的方式上都不同。跨度>
  • 另外,如果您要编写动态编程解决方案,最好在编写任何代码之前确保您的计划在纸上有效。否则你只会用无效的解决方案陷入困境。
  • 这是我第一次看到signed main() :)
  • @PaulMcKenzie 是的,您是对的,代码错误:/。但是我已经添加了答案,为什么我的代码在问题中是错误的,什么是正确的代码。谢谢您的帮助。

标签: c++ string dynamic-programming


【解决方案1】:

这个问题是基于 DP ON INTERVALS 的最难(迄今为止见过)和最美丽(迄今为止再次见过)的问题。
初始代码肯定行不通,因为它只考虑字符串的单次传递,并且在一次又一次地删除模式后不会考虑剩余的字符串。
有 3 种情况:-
情况 1 任何一个字符都没有被删除。
情况 2它作为连续子字符串的一部分被删除。
案例3它作为子序列的一部分被删除,匹配模式集中给定的任何单词,并且不属于该子序列的所有内容首先作为子字符串删除(再次属于单词集) .
第三部分是最棘手的部分,需要足够的思考,实施起来也更加困难。
所以对于每个子串,我们需要检查这个子串是否可以被完全销毁。 compute_full_recur() 函数是确保在 Case 2Case 3 中是否可以删除子字符串的函数。
函数 compute_full 负责案例 1
最后,此代码不会在 codechef 链接上运行,因为所有函数都是递归的,但要验证代码是否正常工作,我已经在问题上运行它Hackerrank 的Reduco,与较低的约束完全相同。下载测试用例,然后在您的 PC 上运行测试用例进行验证。

#include <iostream>
#include <vector>
#include <string>
using namespace std;
#define mx 252
#define nx 40
bool full[mx][mx],vis[mx][mx],full_recur[mx][mx][nx][nx];
int ans[mx];

void init()
{
    for(int i=0;i<mx;i++)
    {
        for(int j=0;j<mx;j++)
        {
            full[i][j]=false,vis[i][j]=false;
        }
    }

    for(int i=0;i<mx;i++)
    {
        ans[i]=-1;
    }

    for(int i=0;i<mx;i++)
    {
        for(int j=0;j<mx;j++)
        {
            for(int k=0;k<nx;k++)
            {
                for(int l=0;l<nx;l++)
                {
                    full_recur[i][j][k][l]=false;
                }
            }
        }
    }

}

bool compute_full_recur(string str,int low,int high,vector<string> pat,int idx,int len)
{
    if(low>high&&len==pat[idx].length())
    {
        return true;
    }
    if(low>high&&len<pat[idx].length())
    {
        full_recur[low][high][idx][len]=false;
        return false;
    }
    if(str[low]==pat[idx][len]&&compute_full_recur(str,low+1,high,pat,idx,len+1))
    {
        return full_recur[low][high][idx][len]=true;
    }
    for(int i=low+1;i<=high;i++)
    {
        if(str[low]==pat[idx][len]&&full[low+1][i]&&compute_full_recur(str,i+1,high,pat,idx,len+1))
        {
            return full_recur[low][high][idx][len]=true;
        }
    }
    full_recur[low][high][idx][len]=false;
    return false;
}

void compute_full(string str,int low,int high,vector<string> pats)
{
    if(low>high)
    {
        return;
    }
    if(vis[low][high])
    {
        return;
    }
    vis[low][high]=true;
    compute_full(str,low+1,high,pats);
    compute_full(str,low,high-1,pats);
    for(int i=0;i<pats.size();i++)
    {
        if(!full[low][high])
        full[low][high]=compute_full_recur(str,low,high,pats,i,0);
    }
}

int compute_ans(string str,int low,int high)
{
    if(low>high)
    {
        return 0;
    }
    if(ans[low]!=-1)
    {
        return ans[low];
    }
    int sol=1+compute_ans(str,low+1,high);
    for(int i=low+1;i<=high;i++)
    {
        if(full[low][i]==true)
        {
            sol=min(sol,compute_ans(str,i+1,high));
        }
    }
    return ans[low]=sol;
}

signed main()
{
    int t;
    cin>>t;
    while(t--)
    {
        string str;
        int n;
        vector<string> pats;
        cin>>n>>str;
        for(int i=0;i<n;i++)
        {
            string tem;
            cin>>tem;
            pats.push_back(tem);
        }
        init();
        compute_full(str,0,str.length()-1,pats);
        cout<<compute_ans(str,0,str.length()-1)<<endl;
    }
    return 0;
}

【讨论】:

  • 答案被否决了,尽管它已经过验证但没关系。唯一的问题是为什么问题被否决了。它本可以帮助某人。:)
最近更新 更多