3個算法 - 口算難題


題目

給你一個方程,左邊用 words 表示,右邊用 result 表示。

你需要根據以下規則檢查方程是否可解:

每個字符都會被解碼成一位數字(0 - 9)。
每對不同的字符必須映射到不同的數字。
每個 words[i] 和 result 都會被解碼成一個沒有前導零的數字。
左側數字之和(words)等於右側數字(result)。
如果方程可解,返回 True,否則返回 False。

示例說明
示例 1:

輸入:words = [“SEND”,“MORE”], result = “MONEY”
輸出:true
解釋:映射 ‘S’-> 9, ‘E’->5, ‘N’->6, ‘D’->7, ‘M’->1, ‘O’->0, ‘R’->8, ‘Y’->‘2’
所以 “SEND” + “MORE” = “MONEY” , 9567 + 1085 = 10652
示例 2:

輸入:words = [“SIX”,“SEVEN”,“SEVEN”], result = “TWENTY”
輸出:true
解釋:映射 ‘S’-> 6, ‘I’->5, ‘X’->0, ‘E’->8, ‘V’->7, ‘N’->2, ‘T’->1, ‘W’->‘3’, ‘Y’->4
所以 “SIX” + “SEVEN” + “SEVEN” = “TWENTY” , 650 + 68782 + 68782 = 138214
示例 3:

輸入:words = [“THIS”,“IS”,“TOO”], result = “FUNNY”
輸出:true
示例 4:

輸入:words = [“LEET”,“CODE”], result = “POINT”
輸出:false

提示:

2 <= words.length <= 5
1 <= words[i].length, results.length <= 7
words[i], result 只含有大寫英文字母
表達式中使用的不同字符數最大爲 10

思路

如果將字符當作一個變量,整個算式就相當於一個多元一次方程,題目就可以看做是,求解10以內整數的多元一次方程,那麼求解步驟就分爲兩步:

  1. 將字符串整理爲多元一次方程,即把字符本身當作變量,而字符所在位置的10進制和當作參數
  2. 求解多元一次方程,這個地方就需要想辦法減少計算了,下面給了三個解法,用於求解多元一次方程。
    在這裏插入圖片描述

解法一

這個是我第一次的寫法,僅通過少量的測試用例,未通過的測試用例是因爲超時了,後面通過解法2和解法3分別對其算法和數據結構做了優化,超時測試用例及實現源碼如下:
在這裏插入圖片描述

[“LEET”,“CODE”]
“POINT”

public class Solution {
        Dictionary<char, int> sList = new Dictionary<char, int>();
        Dictionary<char, int> rList = new Dictionary<char, int>();
        Dictionary<char, int> kList = new Dictionary<char, int>();
        bool[] used = new bool[10];
        char[] kArray;
        public bool IsSolvable(string[] words, string result)
        {
            for (int i = 0; i < 10; i++)
            {
                used[i] = false;
            }
            //整理爲多元一次方程
            for (int i = 0; i < words.Length; i++)
            {
                getFunction(words[i], sList);
            }
            getFunction(result, rList);
            //獲取變量總數
            getKeys(kList, sList);
            getKeys(kList, rList);
            kArray = kList.Keys.ToArray<char>();
            //求解方程
            return this.findSolution(0);
        }

        public void getFunction(string str, Dictionary<char, int> chList)
        {//計算多元一次方程的常量
            int tmpIdx = 1;
            for (int j = str.Length - 1; j >= 0; j--)
            {
                if (chList.ContainsKey(str[j]))
                {
                    chList[str[j]] += tmpIdx;
                }
                else
                {
                    chList[str[j]] = tmpIdx;
                }
                tmpIdx *= 10;
            }
        }

        public void getKeys(Dictionary<char, int> kList, Dictionary<char, int> sList)
        {//獲取多元變量本身
            foreach (char k in sList.Keys)
            {
                if (!kList.ContainsKey(k))
                {
                    kList.Add(k, 0);
                }
            }
        }

        public bool checkEqual(Dictionary<char, int> kList)
        {//判斷方程兩邊是否相等
            int sInt = 0, rInt = 0;
            foreach (char c in sList.Keys)
            {
                sInt += sList[c] * kList[c];
            }
            foreach (char c in rList.Keys)
            {
                rInt += rList[c] * kList[c];
            }
            return rInt == sInt;
        }

        public bool findSolution(int idx)
        {//實際求解
            if (idx == kArray.Length)
                return checkEqual(kList);
            for (int j = 0; j <= 9; j++)
            {
                if (!used[j])
                {
                    kList[kArray[idx]] = j;
                    used[j] = true;
                    if (findSolution(idx+1))
                        return true;
                    used[j] = false;
                }
            }
            return false;
        }
}

解法二

當前解法基於上面的第一種解法做了下面3個角度的優化,來減少運算內容,從而減少時間複雜度。不過,這個解法通過了大部分測試用例,但提交還是提示超時,但單獨執行超時的最後一個測試用例顯示150ms,可能還有個全部測試用例的整體執行時間約束:

  1. 直接將等式兩邊值都合併到左邊,只是等式右邊符號設爲負數,減少了查看變量個數的步驟(getKeys函數);
  2. 求解過程的判斷直接求和,判斷整體是否爲0,不需要再進行對比計算(checkEqual函數);
  3. 不需要先記錄下來每個變量是多少,然後再進行計算,而是在查詢的時候直接計算(kList[kArray[idx]] = j;語句)
    在這裏插入圖片描述

[“YOUVE”,“NEVER”,“BEEN”,“TO”]
“EUROPE”

public class Solution {
    Dictionary<char, int> sList = new Dictionary<char, int>();
    bool[] used = new bool[10];
    char[] kArray;
    public bool IsSolvable(string[] words, string result)
    {
        for (int i = 0; i < 10; i++)
        {
            used[i] = false;
        }
        //整理爲多元一次方程
        for (int i = 0; i < words.Length; i++)
        {
            getFunction(words[i], sList,1);
        }
        getFunction(result, sList, -1);
        //獲取變量總數
        kArray = sList.Keys.ToArray<char>();
        //求解方程
        return this.findSolution(0,0);
    }

    public void getFunction(string str, Dictionary<char, int> chList, int dir)
    {//計算多元一次方程的常量
        int tmpIdx = 1;
        for (int j = str.Length - 1; j >= 0; j--)
        {
            if (chList.ContainsKey(str[j]))
            {
                chList[str[j]] += tmpIdx * dir;
            }
            else
            {
                chList[str[j]] = tmpIdx * dir;
            }
            tmpIdx *= 10;
        }
    }
    
    public bool findSolution(int idx,int sum)
    {//實際求解
        if (idx == kArray.Length)
            return sum == 0;
        for (int j = 0; j <= 9; j++)
        {
            if (!used[j])
            {
                used[j] = true;
                if (findSolution(idx + 1, sum + j * sList[kArray[idx]]))
                    return true;
                used[j] = false;
            }
        }
        return false;
    }
}

解法三

基於第二個解法,做了一個優化,將字典用數組替換掉,由於方案本身並不關心具體某個字母是由哪個數值來代表,只要分別爲不同的字母使用不同的數組索引即可,這樣做減少的步驟是,將通過字典查找值的方式,使用數組索引代替,源碼如下:

public class Solution {
    public bool IsSolvable(string[] words, string result)
    {
        bool[] used = new bool[10];
        Dictionary<char, int> sList = new Dictionary<char, int>();
        for (int i = 0; i < 10; i++)
        {
            used[i] = false;
        }
        //整理爲多元一次方程
        for (int i = 0; i < words.Length; i++)
        {
            getFunction(words[i], sList,1);
        }
        getFunction(result, sList, -1);
        //獲取變量總數
        int[]vArray = sList.Values.ToArray<int>();
        //求解方程
        return this.findSolution(0, 0, vArray,used);
    }

    public void getFunction(string str, Dictionary<char, int> chList, int dir)
    {//計算多元一次方程的常量
        int tmpIdx = 1;
        for (int j = str.Length - 1; j >= 0; j--)
        {
            if (chList.ContainsKey(str[j]))
            {
                chList[str[j]] += tmpIdx * dir;
            }
            else
            {
                chList[str[j]] = tmpIdx * dir;
            }
            tmpIdx *= 10;
        }
    }

    public bool findSolution(int idx, int sum, int[] vArray, bool[] used)
    {//實際求解
        if (idx == vArray.Length)
            return sum == 0;
        for (int j = 0; j <= 9; j++)
        {
            if (!used[j])
            {
                used[j] = true;
                if (findSolution(idx + 1, sum + j * vArray[idx],vArray,used))
                    return true;
                used[j] = false;
            }
        }
        return false;
    }
}

原文地址:https://www.zhenxiangsimple.com/2019/12/30/tech/math-stringCalc/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章