从Google Code Jam 2009解决“欢迎使用Code Jam”

时间:2012-03-17 10:53:42

标签: c++

我正在尝试解决以下代码堵塞问题,我已经取得了一些进展但是在极少数情况下我的代码提供了错误的输出.. Welcome to Code jam

所以我偶然发现了来自俄罗斯的dev“rem”的解决方案。 我不知道他/她的解决方案是如何正常工作的......代码......

const string target = "welcome to code jam";

char buf[1<<20];

int main() {
        freopen("input.txt", "rt", stdin);
        freopen("output.txt", "wt", stdout);

        gets(buf);
        FOR(test, 1, atoi(buf)) {
                gets(buf);
                string s(buf);
                int n = size(s);
                int k = size(target);
                vector<vector<int> > dp(n+1, vector<int>(k+1));
                dp[0][0] = 1;
                const int mod = 10000;
                assert(k == 19);
                REP(i, n) REP(j, k+1) {// Whats happening here
                        dp[i+1][j] = (dp[i+1][j]+dp[i][j])%mod;
                        if (j < k && s[i] == target[j])
                                dp[i+1][j+1] = (dp[i+1][j+1]+dp[i][j])%mod;
                }
                printf("Case #%d: %04d\n", test, dp[n][k]);
        }

        exit(0);
}//credit rem

有人可以解释两个循环中发生的事情吗?

感谢。

2 个答案:

答案 0 :(得分:3)

他在做什么:动态编程,你也可以看到这一点。

他有2D数组,你需要了解它的语义是什么。 事实是dp[i][j]使用输入字符串中的所有字母直到第i个索引来计算他可以获得jwelcome to code jam个字母的子序列的数量。两个索引都是基于1的,以允许不从字符串中取任何字母的情况。

例如,如果输入为:

welcome to code jjam

dp在不同情况下的值将是:

 dp[1][1] = 1; // first letter is w. perfect just the goal
 dp[1][2] = 0; // no way to have two letters in just one-letter string
 dp[2][2] = 1; // again: perfect
 dp[1][2] = 1; // here we ignore the e. We just need the w.
 dp[7][2] = 2; // two ways to construct we: [we]lcome and [w]elcom[e].

您特别询问的循环根据已计算的值计算新的动态值。

答案 1 :(得分:1)

哇,我几天前正在练习这个问题,偶然发现了这个问题。

我怀疑如果不学习DP,说“他正在进行动态编程”并不会解释太多。

我可以提供更清晰的实施和更简单的解释:

string phrase = "welcome to code jam"; // S
string text; getline(cin, text); // T
vector<int> ob(text.size(), 1);
int ans = 0;
for (int p = 0; p < phrase.size(); ++p) {
    ans = 0;
    for (int i = 0; i < text.size(); ++i) {
        if (text[i] == phrase[p]) ans = (ans + ob[i]) % 10000; 
        ob[i] = ans;
    }
}
cout << setfill('0') << setw(4) << ans << endl;

要解决问题,如果S只有一个字符S[0],我们就可以计算其出现次数。

如果它只有两个字符S[0..1],我们会看到每个匹配项T[i]==S[1]都会增加答案[{1}}在索引S[0]之前的出现次数。

对于三个字符i,每次出现S[0..2]同样会在索引T[i]==S[2]之前按S[0..1]出现次数增加答案。此数字与上一段处理i时的答案值相同。

如果有四个字符,答案会在每个找到第四个字符的索引之前的前三个出现次数增加,依此类推。

由于每个其他步骤都使用前一个步骤中的值,因此可以逐步解决。在每个步骤T[i]上,我们需要知道在任何索引p之前出现的前一个子串S[0..p-1]的出现次数,这些索引可以保存在与{0}相同长度的整数数组i中。 T.每当我们在ob遇到ob[i]时,答案就会S[p]。要为下一步准备i,我们还会将每个ob更新为ob[i]的出现次数 - 即更新为当前答案值。

最后,最新答案值(以及S[0..p]的最后一个元素)包含整个ob中整个S的出现次数,这是最终答案。< / p>

请注意,它以T填充的那些开头。第一步与其他步骤不同;但计算ob的出现次数意味着在每次出现时增加S[0]的答案,这是所有其他步骤所做的,除了它们增加1。因此,当每个ob[i]最初为ob[i]时,第一步将与所有其他步骤一样运行,使用相同的代码。