解决 Google Code Jam 2009 中的"Welcome to Code Jam"

Solving "Welcome to Code Jam" from Google Code Jam 2009

本文关键字:Jam Code Welcome to 2009 Google 解决 中的      更新时间:2023-10-16

我正在尝试解决以下代码堵塞问题,我取得了一些进展,但在少数情况下,我的代码给出了错误的输出。欢迎来到代码开发大赛

所以我偶然发现了来自俄罗斯的开发"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: %04dn", test, dp[n][k]);
        }
        exit(0);
}//credit rem

有人可以解释一下两个循环中发生了什么吗?

谢谢。

他在做什么:动态编程,到目前为止你也可以看到。

他有2D数组,你需要了解它的语义是什么。事实是,dp[i][j]计算他可以使用输入字符串中的所有字母获取welcome to code jamj 个字母的子序列的方法数量,直到第 i 个索引。两个索引都是从 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].

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

哇,

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

我怀疑说"他在做动态编程"不会解释太多,如果你没有学习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]索引 i 之前S[0]出现的次数来增加答案。

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

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

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

到最后,最新的答案值(以及ob的最后一个元素(包含整T中整S的出现次数,这就是最终答案。

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