網教9. 一夜發白《千字文》

</pre>背景</h2><p style="margin-top:0px; margin-bottom:10px; color:rgb(68,68,68); font-family:open_sansregular,Arial,Helvetica,sans-serif; font-size:13px; line-height:25px">在古代中國,《三字經》、《百家姓》、《千字文》被合稱爲三、百、千,都是非常重要的啓蒙教育課本,廣爲流傳。而其中問世最早的《千字文》更憑藉其優美的文字、華麗的辭藻成爲中華傳統文化的一個重要組成部分,得到了人們的普遍重視和喜愛。</p><p style="margin-top:0px; margin-bottom:10px; margin-left:40px; color:rgb(68,68,68); font-family:open_sansregular,Arial,Helvetica,sans-serif; font-size:13px; line-height:25px">《千字文》的作者,是梁武帝時代官拜散騎員外郎的周興嗣。歷來在正史上的記載,就這樣一筆帶過,但據私家筆記的野史記載,內容不是這樣簡單了。周興嗣同梁武帝本來便是文字之交的朋友,在蕭齊時代,還在朝廷上有過同僚之誼。到了梁武帝當了皇帝,那就變成君臣的關係。由朋友變君臣,說是關係不錯,其實,伴君如伴虎,反是最糟糕的事,周興嗣有一次不小心得罪了梁武帝,梁武帝一怒之下,想殺他或很嚴厲地處分他,到底還是於心不忍,只好下令把先關起來再說。但梁武帝又說了一句話,你不是文才很好嗎?你能在一夜之間,把一千個不同的字,寫一篇好文章,就赦你無罪。因此,周興嗣就在一夜之間,挖空心思,寫了這篇《千字文》。文章寫好了,可是在一夜之間,頭髮、眉毛、鬍子也都白了!<strong>大家要注意,用一千個不同的中文字,一夜之間,寫出有關宇宙、物理、人情、世故的文間,等於寫了一篇非常精簡的“中國文化綱領要點”,雖然,只寫到南北時期的梁朝爲止,實在也太難了。</strong>梁武帝本人,才華文學都自命不凡,看了周興嗣一夜之間之間所寫的《千字文》,也不能不佩服。周興嗣因此得到寬恕,而且還特加賞賜。</p><p style="margin-top:0px; margin-bottom:10px; margin-left:40px; color:rgb(68,68,68); font-family:open_sansregular,Arial,Helvetica,sans-serif; font-size:13px; line-height:25px; text-align:right">——摘自南懷瑾《原本大學微言》</p><p style="margin-top:0px; margin-bottom:10px; color:rgb(68,68,68); font-family:open_sansregular,Arial,Helvetica,sans-serif; font-size:13px; line-height:25px">現代人,一生中有機會通讀千字文的機會是非常少的,很多理工科的大學生,完全沒有聽說過這篇神奇的文章,自然也會抱着“懷疑一切”的態度問,真的有這麼神嗎,真的一個重複的字都沒有嗎?耳聽爲虛,眼見爲實,我們就來檢驗一下千字文中是不是真的沒有重複的字。</p><p style="margin-top:0px; margin-bottom:10px; color:rgb(68,68,68); font-family:open_sansregular,Arial,Helvetica,sans-serif; font-size:13px; line-height:25px">不過在動手之前,先讓我們花上幾分鐘的時間,讀讀《千字文》吧:<a target=_blank target="_blank" href="http://online.bit.edu.cn/moodle/mod/page/view.php?id=52893" style="color:rgb(67,142,185); text-decoration:none">簡體中文版</a>、<a target=_blank target="_blank" href="http://online.bit.edu.cn/moodle/mod/page/view.php?id=52894" style="color:rgb(67,142,185); text-decoration:none">正體中文版</a>。</p><h2 style="margin:10px 0px; font-family:open_sansbold,sans-serif; line-height:40px; color:rgb(85,85,85); font-size:28px; font-weight:normal!important">任務</h2><p style="margin-top:0px; margin-bottom:10px; color:rgb(68,68,68); font-family:open_sansregular,Arial,Helvetica,sans-serif; font-size:13px; line-height:25px">請編寫一個程序,從輸入中讀取一篇中文文章,並統計出該文章中 ASCII 字符以外的重複出現的每一個字重複出現的次數。</p><p style="margin-top:0px; margin-bottom:10px; color:rgb(68,68,68); font-family:open_sansregular,Arial,Helvetica,sans-serif; font-size:13px; line-height:25px">文章使用 UTF-8 編碼,可能會出現任何可以用 UTF-8 編碼表示的字符(不限於中文)。</p><p style="margin-top:0px; margin-bottom:10px; color:rgb(68,68,68); font-family:open_sansregular,Arial,Helvetica,sans-serif; font-size:13px; line-height:25px"><span style="color:rgb(255,0,0)">文章中所有的字符在 UCS-2 能夠表示的範圍內,即字符的 Unicode 值用兩個字節就可以表示。</span></p><h2 style="margin:10px 0px; font-family:open_sansbold,sans-serif; line-height:40px; color:rgb(85,85,85); font-size:28px; font-weight:normal!important">輸入</h2><p style="margin-top:0px; margin-bottom:10px; color:rgb(68,68,68); font-family:open_sansregular,Arial,Helvetica,sans-serif; font-size:13px; line-height:25px">一篇文章,總字數不限、每行字符數不限。每個字重複出現的次數不超過 60000 次。</p><h2 style="margin:10px 0px; font-family:open_sansbold,sans-serif; line-height:40px; color:rgb(85,85,85); font-size:28px; font-weight:normal!important">輸出</h2><p style="margin-top:0px; margin-bottom:10px; color:rgb(68,68,68); font-family:open_sansregular,Arial,Helvetica,sans-serif; font-size:13px; line-height:25px">按照 Unicode 編碼從小到大的順序,輸出文章中 ASCII <span style="color:blue">(0~127)</span> 字符以外的每一個重複出現過的字重複出現的次數。每行包含三項內容,首先是重複的字符(以 UTF-8 編碼輸出),然後是該字符的 Unicode 編碼值(十六進制輸出,字母均使用小寫,長度不足4位數的用0補齊),最後輸出該字符的重複次數。</p><p style="margin-top:0px; margin-bottom:10px; color:rgb(68,68,68); font-family:open_sansregular,Arial,Helvetica,sans-serif; font-size:13px; line-height:25px">如果文章中沒有出現重複的字,則輸出“No repeat!”。</p><p style="margin-top:0px; margin-bottom:10px; color:rgb(68,68,68); font-family:open_sansregular,Arial,Helvetica,sans-serif; font-size:13px; line-height:25px">由於網頁中的樣例輸入輸出很奇怪,所以就不在這裏貼了……</p><p style="margin-top:0px; margin-bottom:10px; color:rgb(68,68,68); font-family:open_sansregular,Arial,Helvetica,sans-serif; font-size:13px; line-height:25px"></p><p style="margin-top:0px; margin-bottom:10px; color:rgb(68,68,68); font-family:open_sansregular,Arial,Helvetica,sans-serif; font-size:13px; line-height:25px">題解:</p><p style="margin-top:0px; margin-bottom:10px; color:rgb(68,68,68); font-family:open_sansregular,Arial,Helvetica,sans-serif; font-size:13px; line-height:25px">首先推薦一下大神的博客:<a target=_blank target="_blank" href="http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html">點擊打開鏈接</a> 只用看utf-8的部分就可以了.</p><p style="margin-top:0px; margin-bottom:10px; color:rgb(68,68,68); font-family:open_sansregular,Arial,Helvetica,sans-serif; font-size:13px; line-height:25px"><span style="color:rgb(17,17,17); font-family:Consolas,Monaco,'Andale Mono',monospace; font-size:16px; line-height:28px; word-spacing:2px; background-color:rgb(245,242,240)">Unicode符號範圍 | UTF-8編碼方式</span><br style="color:rgb(17,17,17); font-family:Consolas,Monaco,'Andale Mono',monospace; font-size:16px; line-height:28px; word-spacing:2px; background-color:rgb(245,242,240)" /><span style="color:rgb(17,17,17); font-family:Consolas,Monaco,'Andale Mono',monospace; font-size:16px; line-height:28px; word-spacing:2px; background-color:rgb(245,242,240)">(十六進制) | (二進制)</span><br style="color:rgb(17,17,17); font-family:Consolas,Monaco,'Andale Mono',monospace; font-size:16px; line-height:28px; word-spacing:2px; background-color:rgb(245,242,240)" /><span style="color:rgb(17,17,17); font-family:Consolas,Monaco,'Andale Mono',monospace; font-size:16px; line-height:28px; word-spacing:2px; background-color:rgb(245,242,240)">--------------------+---------------------------------------------</span><br style="color:rgb(17,17,17); font-family:Consolas,Monaco,'Andale Mono',monospace; font-size:16px; line-height:28px; word-spacing:2px; background-color:rgb(245,242,240)" /><span style="color:rgb(17,17,17); font-family:Consolas,Monaco,'Andale Mono',monospace; font-size:16px; line-height:28px; word-spacing:2px; background-color:rgb(245,242,240)">0000 0000-0000 007F | 0xxxxxxx</span><br style="color:rgb(17,17,17); font-family:Consolas,Monaco,'Andale Mono',monospace; font-size:16px; line-height:28px; word-spacing:2px; background-color:rgb(245,242,240)" /><span style="color:rgb(17,17,17); font-family:Consolas,Monaco,'Andale Mono',monospace; font-size:16px; line-height:28px; word-spacing:2px; background-color:rgb(245,242,240)">0000 0080-0000 07FF | 110xxxxx 10xxxxxx</span><br style="color:rgb(17,17,17); font-family:Consolas,Monaco,'Andale Mono',monospace; font-size:16px; line-height:28px; word-spacing:2px; background-color:rgb(245,242,240)" /><span style="color:rgb(17,17,17); font-family:Consolas,Monaco,'Andale Mono',monospace; font-size:16px; line-height:28px; word-spacing:2px; background-color:rgb(245,242,240)">0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx</span><br style="color:rgb(17,17,17); font-family:Consolas,Monaco,'Andale Mono',monospace; font-size:16px; line-height:28px; word-spacing:2px; background-color:rgb(245,242,240)" /><span style="color:rgb(17,17,17); font-family:Consolas,Monaco,'Andale Mono',monospace; font-size:16px; line-height:28px; word-spacing:2px; background-color:rgb(245,242,240)">0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx</span></p><p style="margin-top:0px; margin-bottom:10px">所以文字在輸入時有四種形式,而具體是哪種需要判斷。如果輸入的第一個值小於128,則是第一種;大於128小於192則是第二種,大於192小於224是第三種,剩下一個就是第四種。使用位運算可以更加直觀地判斷:(首先讀入a)(a>>4)==15是第四種,(a>>4)==14是第三種,(a>>5)==6是第二種,剩下的是第一種。而第一種又不需要計數。</p><p style="margin-top:0px; margin-bottom:10px">知道了文字編碼格式後,就看一下這些數字具體是怎麼儲存的。</p><p style="margin-top:0px; margin-bottom:10px"><span style="color:rgb(17,17,17); font-family:Georgia,serif; font-size:16px; line-height:28px; word-spacing:2px; background-color:rgb(245,245,213)">已知"嚴"的unicode是4E25(100111000100101),根據上表,可以發現4E25處在第三行的範圍內(0000 0800-0000 FFFF),因此"嚴"的UTF-8編碼需要三個字節,即格式是"1110xxxx 10xxxxxx 10xxxxxx"。然後,從"嚴"的最後一個二進制位開始,依次從後向前填入格式中的x,多出的位補0。</span></p><p style="margin-top:0px; margin-bottom:10px">也就是說,讀入a,b,c之後,還要算出來xxxx是多少,然後再乘上它們的位。第一個xxxx是(a-2^7-2^6-2^5),第二個是(b-2^7),第三個是(c-2^7),最後知道這個數的unicode編碼是(a-2^7-2^6-2^5)*2^6*2^6+(b-2^7)*2^6+(c-2^7),最後再儲存到數組裏。輸出的時候再算回來,輸出編號,unicode號和出現的次數即可。</p><p style="margin-top:0px; margin-bottom:10px">這個題的難點在於理解編碼的形式。</p><p style="margin-top:0px; margin-bottom:10px">AC代碼:</p><p style="margin-top:0px; margin-bottom:10px; color:rgb(68,68,68); font-family:open_sansregular,Arial,Helvetica,sans-serif; font-size:13px; line-height:25px"></p><pre name="code" class="cpp">/*http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html*/  
#include<stdio.h>  
#include<string.h>  
#include<locale.h>  
#include<wchar.h>  
struct node  
{  
    int a;  
    unsigned char s[5];  
}vis[(1<<16)+5];//記錄英文字母的方法是長度26的數組,記錄漢字就用65536的數組  
  
int main()  
{  
//  freopen("F:in.txt", "r", stdin);  
//  freopen("F:out.txt", "w", stdout);  
    unsigned char ch,ch1,ch2,ch3;  
    int i;  
    for (i = 0; i < 65536; i++)  
    {  
        vis[i].a = 0;  
        vis[i].s[0] = '\0';  
    }  
    while (scanf("%c",&ch)!=EOF)  
    {  
        int t=ch,t0 = ch, t1 = 0, t2 = 0, t3 = 0;  
        if ((ch>>4)==15)  
        {  
            scanf("%c%c%c", &ch1,&ch2,&ch3);  
            t1 = ch1; t2 = ch2; t3 = ch3;  
            t = (t0 - 128 - 64 - 32-16) * 64 * 64 * 64 + (t1 - 128) * 64 * 64 + (t2 - 128) * 64 + t3 - 128;  
            vis[t].s[0] = ch; vis[t].s[1] = ch1; vis[t].s[2] = ch2; vis[t].s[3] = ch3; vis[t].s[4] = '\0';  
        }  
        else if ((ch>>4)==14)  
        {  
            scanf("%c%c", &ch1, &ch2);  
            t1 = ch1; t2 = ch2;  
            t = (t0 - 128 - 64 - 32) * 64 * 64 + (t1 - 128) * 64 + t2 - 128;  
            vis[t].s[0] = ch; vis[t].s[1] = ch1; vis[t].s[2] = ch2; vis[t].s[3] = '\0';  
        }  
        else if ((ch >> 5) == 6)  
        {  
            scanf("%c", &ch1);  
            t1 = ch1;  
            t = (t0 - 64 - 128) * 64 + t1 - 128;  
            vis[t].s[0] = ch; vis[t].s[1] = ch1; vis[t].s[2] = '\0';  
        }  
        else  
            t = ch;  
        vis[t].a++;  
    }  
    int flag = 1;  
    for (i = 1; i < 65536; i++)  
    {  
        if (vis[i].a > 1)  
        if (vis[i].s[0]>=128)  
        {  
            printf("%s 0x%04x %d\n", vis[i].s, i, vis[i].a);  
//          wprintf(L"%c 0x%x %d\n", i, i, vis[i]);  
            flag = 0;  
        }  
    }  
    if (flag)  
        printf("No repeat!\n");  
    return 0;  
}  


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