題目
給你一個方程,左邊用 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以內整數的多元一次方程,那麼求解步驟就分爲兩步:
- 將字符串整理爲多元一次方程,即把字符本身當作變量,而字符所在位置的10進制和當作參數
- 求解多元一次方程,這個地方就需要想辦法減少計算了,下面給了三個解法,用於求解多元一次方程。
解法一
這個是我第一次的寫法,僅通過少量的測試用例,未通過的測試用例是因爲超時了,後面通過解法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,可能還有個全部測試用例的整體執行時間約束:
- 直接將等式兩邊值都合併到左邊,只是等式右邊符號設爲負數,減少了查看變量個數的步驟(getKeys函數);
- 求解過程的判斷直接求和,判斷整體是否爲0,不需要再進行對比計算(checkEqual函數);
- 不需要先記錄下來每個變量是多少,然後再進行計算,而是在查詢的時候直接計算(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/