首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

Google 競賽題 SecretSum C++ 解法:字母數字謎題的高效求解策略

     編輯:關於C++

題目重述

給定三個 3 字母字串 num1、num2、result,每字母代表唯一數字 (A-Z 對應 0-9 不重複),首字母不為 0。求滿足 num1 + num2 = result 的合法數字對應總數。例如 "AAA" + "BBB" = "CCC" 返回 32。

解題框架:兩階段篩選

  1. 候選數生成:枚舉 num1、num2 可能值,推導 result = num1 + num2
  2. 模式匹配驗證:檢查三數字是否符合字串字母模式與唯一性約束

階段一:高效候選數枚舉

範圍縮減

  • 三位數範圍:100~999(首位非 0)
  • num1 + num2 ≤ 999 → num2 ≤ 999 - num1
  • num1 最大 899(當 num2=100 時)
  • 雙重迴圈總組合:Σ(999-i) for i=100..899 = 320,400,遠小於 900×900=810,000

字母模式預過濾

將字串字母關係編碼為 3 位掩碼:


// 位 0: pos0==pos1, 位 1: pos0==pos2, 位 2: pos1==pos2
"ABA" → 010 (二進制) = 2
"AAA" → 111 = 7
"ABC" → 000 = 0

預計算三字串掩碼 pattern[3]。枚舉數字時同樣計算掩碼,不匹配即跳過,大幅減少進入精確驗證的候選數。

階段二:精確模式驗證

座標映射法

將三字串視為 3×3 字母矩陣:


Row 0: num1[0] num1[1] num1[2]
Row 1: num2[0] num2[1] num2[2]
Row 2: res[0]  res[1]  res[2]

建立字母→座標列表映射:letterPos[26] = vector<{row,col}>。僅保留出現過字母的指標陣列 usedLetters。

驗證演算法


bool Verify(int n1, int n2, int n3) {
    char digits[3][4];
    sprintf(digits[0], "%03d", n1);
    sprintf(digits[1], "%03d", n2);
    sprintf(digits[2], "%03d", n3);

    bool used[10] = {false};
    for (auto vecPtr : usedLetters) {
        auto& vec = *vecPtr;
        int d = digits[vec[0].row][vec[0].col] - '0';  // 該字母第一次出現的數字
        if (used[d]) return false;                     // 數字已被其他字母使用
        for (size_t i=1; i

完整核心代碼


class SecretSum {
    int pattern[3];                    // 三字串字母掩碼
    vector> pos[26];   // 字母→座標列表
    vector>*> used; // 出現字母指標陣列

    void Init(const string& n1, const string& n2, const string& r) {
        string s[3] = {n1, n2, r};
        memset(pattern, 0, sizeof(pattern));
        for (int i=0; i<26; ++i) pos[i].clear();
        used.clear();

        for (int i=0; i<3; ++i) {
            for (int j=0; j<3; ++j) {
                char c = s[i][j];
                pos[c-'A'].push_back({char(i), char(j)});
            }
            // 計算掩碼
            if (s[i][0]==s[i][1]) pattern[i] |= 1;
            if (s[i][0]==s[i][2]) pattern[i] |= 2;
            if (s[i][1]==s[i][2]) pattern[i] |= 4;
        }
        for (int i=0; i<26; ++i)
            if (!pos[i].empty()) used.push_back(&pos[i]);
    }

    bool MatchPattern(int idx, int num) {
        char buf[4]; sprintf(buf, "%03d", num);
        int m = 0;
        if (buf[0]==buf[1]) m |= 1;
        if (buf[0]==buf[2]) m |= 2;
        if (buf[1]==buf[2]) m |= 4;
        return m == pattern[idx];
    }

    bool Verify(int n1, int n2, int n3) { /* 上述驗證邏輯 */ }

public:
    int countPossible(string n1, string n2, string r) {
        Init(n1, n2, r);
        int count = 0;
        for (int i=100; i<=899; ++i) {
            if (!MatchPattern(0, i)) continue;
            for (int j=100; j<=999-i; ++j) {
                if (!MatchPattern(1, j)) continue;
                int k = i + j;
                if (!MatchPattern(2, k)) continue;
                if (Verify(i, j, k)) ++count;
            }
        }
        return count;
    }
};

效能分析與優化方向

測例模式Verify 呼叫次數備註
Example 5全 000 模式117,662最慢,無字母重複無法預過濾
Example 4result=1112,144result 掩碼 111 強力過濾

優化思路:

  1. 預計算合法數字表:對每種掩碼 (0~7) 預建合法三位數向量,迴圈直接遍歷向量而非 100~999
  2. 提前綁定高頻字母:優先處理出現次數最多的字母,失敗快速剪枝
  3. 位運算加速:用 10 位位掩碼替代 bool used[10]

題目測試結果驗證


"AAA"+"BBB"="CCC"  → 32  ✓
"ABB"+"DEE"="TTT"  → 112 ✓
"ABC"+"ABA"="ACC"  → 0   ✓
"AAA"+"CDD"="BAA"  → 32  ✓
"TEF"+"FET"="AAA"  → 12  ✓
"ABC"+"ABC"="BCE"  → 5   ✓
"AAA"+"AAA"="BBB"  → 4   ✓

總結

本解法採用範圍縮減 + 掩碼預過濾 + 座標映射精確驗證三層策略,將搜尋空間從 81萬壓縮至最差 16萬、典型情況千級。代碼結構清晰、易擴充至任意長度字串(修改矩陣維度即可),展示了組合優化在約束滿足問題中的威力。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved