hdu 4300 Clairewd’s message(扩展kmp)

Clairewd is a member of FBI. After several years concealing in BUPT, she intercepted some important messages and she was preparing for sending it to ykwd. They had agreed that each letter of these messages would be transfered to another one according to a conversion table.
Unfortunately, GFW(someone’s name, not what you just think about) has detected their action. He also got their conversion table by some unknown methods before. Clairewd was so clever and vigilant that when she realized that somebody was monitoring their action, she just stopped transmitting messages.
But GFW knows that Clairewd would always firstly send the ciphertext and then plaintext(Note that they won’t overlap each other). But he doesn’t know how to separate the text because he has no idea about the whole message. However, he thinks that recovering the shortest possible text is not a hard task for you.
Now GFW will give you the intercepted text and the conversion table. You should help him work out this problem.
Input
The first line contains only one integer T, which is the number of test cases.
Each test case contains two lines. The first line of each test case is the conversion table S. S[i] is the ith latin letter’s cryptographic letter. The second line is the intercepted text which has n letters that you should recover. It is possible that the text is complete.
Hint
Range of test data:
T<= 100 ;
n<= 100000;
Output
For each test case, output one line contains the shorest possible complete text.
Sample Input

2
abcdefghijklmnopqrstuvwxyz
abcdab
qwertyuiopasdfghjklzxcvbnm
qwertabcde

Sample Output

abcdabcd
qwertabcde

题解:
(我被这道题已经折磨疯了,就因为那个奇怪的bug)
题目给了一串解码,和一串密文加明文,由于明文不全,该题的目的就是补全不全的明文;
由于我们已知密文+明文,记作原串s,利用解码将该句可以解密为明文+乱文,记作模式串str,最终求出s的后缀和str的前缀的最大公共部分,我们就知道了密文和明文的分割点。知道分割点后把剩下的明文补全就好了。因为是后缀匹配前缀,所以可以用扩展KMP解决,因为密文长度一定大于等于明文,所以我们再判断一下分割点的位置是不是在字符串的二分之一之后。

#include<bits/stdc++.h>
using namespace std;

const int maxn = 100050;
int ne[maxn], ex[maxn];

void GetNext(char *b){
    int i = 0, po, j, len = strlen(b);
    ne[0] = len;//初始化next[0]
    while(b[i] == b[i+1] && i+1 < len)
        i++;
    ne[1] = i;
    po = 1;//初始化po的位置
    for(i = 2; i < len; i++){
        if(ne[i-po]+i < ne[po]+po)//第一种情况,可以直接得到next[i]的值
            ne[i] = ne[i-po];
        else{//第二种情况,要继续匹配才能得到next[i]的值
            j = ne[po] + po - i;
            if(j < 0)
                j = 0;//如果i>po+next[po],则要从头开始匹配
            while(i+j < len && b[j] == b[j+i])//计算next[i]
                j++;
            ne[i] = j;
            po = i;//更新po的位置
        }
    }
}

void EXKMP(char *a, char *b){//用密文加明文的后缀匹配明文加密文的前缀
    int i = 0, j, po, lena = strlen(a), lenb = strlen(b);
    GetNext(b);//计算子串的next数组
    while(a[i] == b[i] && i<lena && i<lenb){
        i++;
    }
    ex[0] = i;
    po = 0;//初始化po的位置
    for(i=1; i<lena; i++){
        if(ne[i-po]+i < ex[po]+po)
            ex[i] = ne[i-po];
        else{//第二种情况,要继续匹配才能得到ex[i]的值
            j = ex[po]+po-i;
            if(j<0)
                j = 0;
            while(i+j<lena && j<lenb && a[j+i] == b[j])
                j++;
            ex[i] = j;
            po = i;//更新po的位置
        }
    }
}

int main(){
    int T, i, j;
    char a[maxn], b[maxn], c[maxn];
    scanf("%d", &T);
    map<char, char> mp;
    while(T--){
        scanf("%s%s", a, b);
        int len = strlen(a);
        for(i = 0; i < len; i++){
            mp[a[i]] = 'a'  + i;
        }
        int n = strlen(b);
        for(i = 0; i < n; i++){
            c[i] = mp[b[i]];
        }
        c[n] = 0;
        EXKMP(b, c);
        //求明文的长度
        int s;
        for(s = 0; s < n; s++){
            if(s + ex[s] >= n && s >= (n+1)/2){
                    //s = i;
               // cout << s << endl;
                break;
            }
        }
       // cout << s << endl;
        for(i = 0; i < s; i++){
            printf("%c", b[i]);
        }
        for(i = 0; i < s; i++){
            printf("%c", mp[b[i]]);
        }
        printf("\n");
    }
    return 0;
}

遇到一个神奇的bug,,,有哪位大佬能帮忙给小渣渣解释一番吗~

int s;
for(s = 0; s < n; s++){
    if(s + ex[s] >= n && s >= (n+1)/2){
                    //s = i;
               // cout << s << endl;
         break;
    }
}
//这样是对的
int s;
for(i = 0; i < n; i++){
    if(i + ex[i] >= n && i >= (n+1)/2){
                s = i;
               // cout << s << endl;
         break;
    }
}
//这样是错的 why???

猜你喜欢

转载自blog.csdn.net/ling_wang/article/details/81174488