牛客網-psd面試(最長公共子序列&迴文串)

連接:https://www.nowcoder.com/acm/contest/90/D
來源:牛客網web

掌握將來命運的女神 psd 師兄在拿了朝田詩乃的 buff 後決定去實習。
埃森哲公司註冊成立於愛爾蘭,是一家全球領先的專業服務公司,爲客戶提供戰略、諮詢、數字、技術和運營服務及解決方案。他們立足商業與技術的前沿,業務涵蓋40多個行業,以及企業平常運營部門的各個職能。憑藉獨特的業內經驗與專業技能,以及翹楚全球的交付網絡,他們幫助客戶提高績效,併爲利益相關方持續創造價值。埃森哲是《財富》全球500強企業之一,目前擁有約41.1萬名員工,服務於120多個國家的客戶。因而psd打算去埃森哲公司投一下簡歷。
因而他用英文寫了一篇簡歷,因爲手速太快了以至本身都不知道寫了什麼。
然而面試官 xwc 一眼就看到了重點:大學打過 ACM!
xwc:「
據說你很低襖?考你個題:
忽略字母大小寫,你這篇簡歷去掉最長的迴文子序列後還有多長?

psd 順手就把這個問題拋給了你。
輸入描述:
多組輸入,每組輸入一個長度不超過 1234 的沒空格的字符串,是 psd 的簡歷。
輸出描述:
每組輸出一個整數,如題。
示例1
輸入
google
輸出
2
示例2
輸入
aBc,bAd
輸出
2面試

所謂求最長迴文子序列就是求原串和原串倒過來的LCS,注意題目中要求除去最長迴文子序列剩下的長度。數組

那麼如何求LCS呢。
DP操做,DP數組中的i和j分別表示在兩串中,分別取到前i位和前j位時,兩個前綴子串所遞推獲得的最長公共子序列。
所以咱們將數組中的0位【i,0】和【0,i】分別初始化爲0,表示不管i是多少,另外一個串前綴取幾位,與當前串的空串都不會有任何公共子序列。
這裏寫圖片描述網絡

接着咱們開始遞推,遍歷i,j以對比兩串中相等的字符,一旦遇到相等的字符,咱們將從i-1,j-1的位置繼承狀態,並延續繼承到的最長公共子序列的記錄值,也就是+1操做,表示,當兩串的第i位字符和第j位字符相等時,將從不包括這兩個字符的前綴子串裏獲得的最長公共子序列+1,獲得當前位置的新的最長公共子序列。也就是求解當前i,j的狀態,來自於i-1,j-1的狀態+1得到。svg

而當i,j位置的字符不等時,當前位不能延續最長公共子序列的值,只能從以前求得的值中繼承。那麼咱們要求最長的公共子序列,就是要繼承全部狀態中最大的值,也就是三個位置中選最大值,即i-1,j 和i,j-1和i-1,j-1三個方向取最大值繼承。這樣遞推,每次都使用最大值,繼承最大值,延續最大值,將獲得的結果也是第lenx,leny處,整個串的最長公共子序列。ui

因而咱們獲得狀態轉移方程:
這裏寫圖片描述google

能夠看到,就像以前說的,只有字符相等時,纔會有+1的延續,並且只從i-1和j-1處繼承,可能你會問,爲何不取3個方向的最大值繼承並延續,是由於咱們當前匹配成功的是第i和j個字符,那麼前i位前綴中已經和前j位前綴獲得了一個最大值,前面提到,咱們從i-1,j-1位繼承,是由於咱們第i位與第j位相等了,咱們此處必定能算上一個公共的子序列,那麼若是除去這個新得到的公共字符,那麼就是i-1,j-1的字符串進行比較,只能從沒有去i和j的前綴中取一個狀態。spa

若是咱們取了i-1,j表示j字符咱們已經取到過了,在這個狀態中即一個串的前j位和另外一個串的前i-1位的最長公共子序列值,那就不存在咱們取j位和i位相等的狀況了,由於j早就被其餘字符所匹配過了。同理i,j-1也是如此,既然這個狀態中i已經被配過,新來 j和誰配?明明和i相等,可是i被別的字符給配走了,沒法造成一個新的公共字符以增加最長公共子序列值。code

總結一下,即增加最長公共子序列只能從i-1,j-1這個狀態繼承並延續,由於要改變這個狀態,須要的是不影響當前匹配狀態的上一個狀態。 而當不匹配當前字符時,咱們選擇延續值最大的那個匹配狀態,也就是進行i-1,j-1 和i-1,j和j,j-1三選一的操做,由於當前已經不匹配了,我要繼續讀取新字符檢查是否匹配,對於廢掉的當前位,咱們不須要對上一個狀態有什麼特殊的要求,所以直接取最大的保留下來便可,爲未來可能出現的公共字符提供一個最優解來延續。xml

就這樣遞推到字符末尾便可獲得兩個完整串的最長公共子序列。

#include<stdio.h>///最長公共子序列
#include<string.h>
#include<algorithm>
using namespace std;
int vis[2008][2009],t;
int main()
{

    char x[2005];
    char y[2004];
    while(scanf("%s",x+1)!=EOF)
    {
        memset(vis,0,sizeof(vis));
        int lx=strlen(x+1);
        for(int i=1; i<=lx; i++)///迴文串子序列,序列中選出一段不連續的子序列使其爲迴文串
            if(x[i]>='a'&&x[i]<='z') x[i]=x[i]-'a'+'A';///將原串倒過來,計算兩串的最長公共子序列即最長迴文串
        int ly=0;
        for(int i=lx; i>=1; i--) y[++ly]=x[i];
// printf("%s \n%s\n",x+1,y+1);
        for(int i=1; i<=lx; i++)///邊界初始化,當空串和有長度的串比較時,公共子序列長度爲0
        {
            vis[i][0]=0;
        }
        for(int i=1; i<=ly; i++)
        {
            vis[0][i]=0;
        }
        vis[0][0]=0;///空串比較空串
        for(int i=1; i<=lx; i++)///dp數組中i和j表示,當一個串長i,另外一個串長j時,在此狀況下的最長公共子序列
        {
            for(int j=1; j<=ly; j++)///所以,每次增長一個串的長度i或j,對比其字符是否相相等的狀況下說明此字符將延續最長公共子序列,繼承i-1,j-1(兩串的字符都不取當前字符的最長值)
            {///當兩當前對比字符相等時,直接從i-1,j-1處的值+1,不從i-1,j和i,j-1處繼承
                if(x[i]!=y[j])
                {///兩字符不等時,從三個方向取最大值來繼承

                    vis[i][j]=vis[i-1][j-1];
                    vis[i][j]=max(vis[i-1][j],max(vis[i][j],vis[i][j-1]));
                }
                else
                    vis[i][j]=vis[i-1][j-1]+1;
            }
        }
// printf(" ");///這是一個很美的動態規劃造成的遞推表格,想要了解過程的能夠解開註釋看一下過程
// for (int i=1; i<=lx; ++i)
// printf(" %c",x[i]);
// puts("");
// for(int i=0; i<=ly; i++)
// {
// printf("%c ",i==0?' ':y[i]);
// for(int j=0; j<=lx; j++)
// printf("%d%c",vis[i][j],j==lx?'\n':' ');
// }
        printf("%d\n",ly-vis[lx][ly]);
    }
    return 0;
}
相關文章
相關標籤/搜索