iT邦幫忙

0

【解題紀錄】身分證規則驗證

  • 分享至 

  • xImage
  •  

嗨,大家好,我是KevinLee,一位正在在轉職軟體工程師路上的遊戲技術美術。
為了能讓自己學習的過程更加深刻,決定開始在這邊記錄自己練習過的題目,方便未來學習更專精時,可以回頭審視自己所寫過的code,也請大家多多指教!


📅 2026/01/012
🏷️ C++、leetcode、Flow Control
✍️ KevinLee
🧩 Topic from "Zerojudge"

🎯 我國的身分證字號檢驗規則如下:
(1)英文代號以下表轉換成數字

  A=10 台北市     J=18 新竹縣     S=26 高雄縣
  B=11 台中市     K=19 苗栗縣     T=27 屏東縣
  C=12 基隆市     L=20 台中縣     U=28 花蓮縣
  D=13 台南市     M=21 南投縣     V=29 台東縣
  E=14 高雄市     N=22 彰化縣     W=32 金門縣
  F=15 台北縣     O=35 新竹市     X=30 澎湖縣
  G=16 宜蘭縣     P=23 雲林縣     Y=31 陽明山
  H=17 桃園縣     Q=24 嘉義縣     Z=33 連江縣
  I=34 嘉義市     R=25 台南縣

(2)英文轉成的數字, 個位數乘9再加上十位數的數字
(3)各數字從右到左依次乘1、2、3、4....8
(4)求出(2),(3) 及最後一碼的和
(5)(4)除10 若整除,則為 real,否則為 fake
例:T112663836
2 + 7x9 + 1x8 + 1x7 + 2x6 + 6x5 + 6x4 + 3x3 + 8x2 + 3x1 + 6 = 180
180除以10整除,因此為"real"
/
輸入說明: 輸入共一行。每一行包含一組身分證號碼
輸出說明: 每讀入一行身分證字號,輸出 real or fake

範例輸入#1: T112663836
範例輸出#1: real
/
範例輸入#2: S154287863
範例輸入#2: fake

📜 邏輯紀錄
看完題目後,我習慣先將所需的變數一一整理並宣告,如下。首先,由於輸入只會有一行,因此宣告一個 string 來接收輸入內容。接著,題目在第 (4) 點中提到需要計算「和」,因此需要一個 int 變數來儲存累加結果。
在處理英文轉數字的部分,我第一時間聯想到可以利用 ASCII 進行轉換。不過進一步思考後發現,英文字母對應的 ASCII 十進位數值並不等於題目所定義的實際數字,例如字母 A 的 ASCII 為 65,但題目中定義A = 10,兩者並不相符。
因此,需要一個int變數來儲存每個英文字母轉換後的數值,於是宣告了一個ENASCII變數。同時觀察到 ASCII 值 65 與題目中的 10 之間相差 55,於是再宣告一個 int 作為偏移值,用來修正 ASCII 與題目數值之間的差距。

string ID;
int Total = 0;
int ENASCII = 0;
int offset = 0;

接著,需要輸入一個字串

cin >> ID;

(2)提到,英文轉成的數字, 個位數乘9再加上十位數的數字,公式如下:
將字串的第一個英文字母轉換成int,可透過int(ID[0])得到該字元的ASCII。

ENASCII = ((int(ID[0]) - offset) % 10) * 9 + (int(ID[0]) - offset) / 10;

(3),各數字從右到左依次乘1、2、3、4....8。這樣的規則非常適合透過迴圈來處理,如下: 由於這邊也是將string中的各個字元轉換成int,因此會遵循ASCII的規則,將字元數字轉換成ASCII,例如1的ASCII是49,因此,需要將其-48才能代表真實的數字。最後在進行(4)的規則,也就是(2)、(3)的總和。

for (int i = 0; i < 8; i++)
    {
        Total = Total + (int(ID[i + 1]) - 48) * (8 - i);
    }
    
    Total = Total + ENASCII + (int(ID[9]) - 48);

(5)提到,將(4)除10 若整除,則為 real,否則為 fake,也就是將Total % 10 = 0即可判斷real or fake。

if (Total % 10 == 0)
    {
        cout << "real" << endl;
    }
    else
    {
        cout << "fake" << endl;
    }

最後,在檢視英文轉數字的轉換規則後可以發現,A ~ Z 所對應的數值並非單純依序遞增,因此各字母所需的偏移值也不盡相同。不過,由於這組對應關係是固定且明確的,在本次實作中,我選擇以最直覺的方式,透過條件式將轉換規則直接寫死來處理。

int ID_Rule(string ID)
{
    int offset = 0;
    if (ID[0] == 'A' || ID[0] == 'B' || ID[0] == 'C' || ID[0] == 'D' || ID[0] == 'E' || ID[0] == 'F' || ID[0] == 'G' || ID[0] == 'H' || ID[0] == 'W')
    {
        offset = 55;
    }
    else if (ID[0] == 'I')
    {
        offset = 39;
    }
    else if (ID[0] == 'J' || ID[0] == 'K' || ID[0] == 'L' || ID[0] == 'M' || ID[0] == 'N')
    {
        offset = 56;
    }
    else if (ID[0] == 'O')
    {
        offset = 44;
    }
    else if (ID[0] == 'P' || ID[0] == 'Q' || ID[0] == 'R' || ID[0] == 'S' || ID[0] == 'T' || ID[0] == 'U' || ID[0] == 'V' || ID[0] == 'Z')
    {
        offset = 57;
    }
    else if (ID[0] == 'X' || ID[0] == 'Y')
    {
        offset = 58;
    }

    return offset;
}

此外,後續也會補充另一種作法,改以 map 進行查表轉換。

完整程式碼如下:

#include <iostream>

using namespace std;

int ID_Rule(string ID)
{
    int offset = 0;
    if (ID[0] == 'A' || ID[0] == 'B' || ID[0] == 'C' || ID[0] == 'D' || ID[0] == 'E' || ID[0] == 'F' || ID[0] == 'G' || ID[0] == 'H' || ID[0] == 'W')
    {
        offset = 55;
    }
    else if (ID[0] == 'I')
    {
        offset = 39;
    }
    else if (ID[0] == 'J' || ID[0] == 'K' || ID[0] == 'L' || ID[0] == 'M' || ID[0] == 'N')
    {
        offset = 56;
    }
    else if (ID[0] == 'O')
    {
        offset = 44;
    }
    else if (ID[0] == 'P' || ID[0] == 'Q' || ID[0] == 'R' || ID[0] == 'S' || ID[0] == 'T' || ID[0] == 'U' || ID[0] == 'V' || ID[0] == 'Z')
    {
        offset = 57;
    }
    else if (ID[0] == 'X' || ID[0] == 'Y')
    {
        offset = 58;
    }

    return offset;
}

int main()
{
    string ID;
    int Total = 0;
    int ENASCII = 0;
    int offset = 0;
    
    cin >> ID;
    offset = ID_Rule(ID);


    ENASCII = ((int(ID[0]) - offset) % 10) * 9 + (int(ID[0]) - offset) / 10;
    for (int i = 0; i < 8; i++)
    {
        Total = Total + (int(ID[i + 1]) - 48) * (8 - i);
    }
    
    Total = Total + ENASCII + (int(ID[9]) - 48);

    if (Total % 10 == 0)
    {
        cout << "real" << endl;
    }
    else
    {
        cout << "fake" << endl;
    }
    
}

/
以下補上改用map的寫法,目前還在適應C++駝峰式命名(Camel Case),跟上面變數命名有些不同,請見諒。

#include <iostream>
#include <string>
#include <map>

using namespace std;

int IdRule(string id)
{
    map<char, int> id_map = {
        {'A', 10}, {'B', 11}, {'C', 12}, 
        {'D', 13}, {'E', 14}, {'F', 15}, 
        {'G', 16}, {'H', 17},{'I', 34}, 
        {'J', 18}, {'K', 19}, {'L', 20}, 
        {'M', 21}, {'N', 22}, {'O', 35}, 
        {'P', 23},{'Q', 24}, {'R', 25}, 
        {'S', 26}, {'T', 27}, {'U', 28}, 
        {'V', 29}, {'W', 32}, {'X', 30},
        {'Y', 31}, {'Z', 33}
    };
    
    return id_map[id[0]];
}

int main()
{
    string id;
    int total = 0;
    int char_id = 0;
    int convertedValue = 0;

    cin >> id;
    char_id = IdRule(id);

    convertedValue = (char_id % 10) * 9 + (char_id) / 10;
    for (int i = 0; i < 8; i++)
    {
        total = total + (int(id[i + 1]) - 48) * (8 - i);
    }
    
    total = total + convertedValue + (int(id[9]) - 48);

    if (total % 10 == 0)
    {
        cout << "real" << endl;
    }
    else
    {
        cout << "fake" << endl;
    }
    
}

圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言