【专题】Subsequence

回文子序列

----------------------------

最长回文子序列

题目略

----------------------------

这是回文子序列的基础,直接DP即可。

f[ L ][ R ] 表示区间[ L , R ] 的最长回文子序列长度。

f[ L ][ R ] = 0 ( L > R )

f[ L ][ R ] = 1 ( L = R )

f[ L ][ R ] = f[ L+1 ][ R-1 ] + 2   ( S[ L ]==S[ R ] )

f[ L ][ R ] = max( f[ L+1 ][ R ] , f[ L ][ R-1 ] )

----------------------------

代码略

----------------------------

环形回文子序列

HDU 4745 Two Rabbits

----------------------------

重点是用next() prev() 代替l+1,r-1

 在普通的回文子序列dp中 若x>y 则 f[ x ][ y ]没有值

这为环形dp提供了储存空间。

----------------------------

const int maxn=1111;
int a[maxn];
int f[maxn][maxn];
int n;
int prev(int x){
    return (x-1+n)%n;
}
int next(int x){
    return (x+1)%n;
}

int dp(int l,int r){
    if (l==r) return 1;
    if (next(l)==r) return 1+(a[l]==a[r]);
    if (f[l][r]!=-1) return f[l][r];
    if (a[l]==a[r]) return f[l][r]=dp(next(l),prev(r))+2;
    return f[l][r]=max(dp(next(l),r),dp(l,prev(r)));
}

int main()
{
    int ans;
    while (~scanf("%d",&n)){
        if (n==0) break;
        ans=0;
        memset(f,-1,sizeof(f));
        for (int i=0;i<n;i++){
            scanf("%d",&a[i]);
        }
        if (n==1) ans=1;
        else if (n==2) ans=2;
        else{
            for (int i=0;i<n;i++){
                ans=max(ans,dp(next(i),prev(i))+1);
                //ans=max(ans,dp(i,prev(i)));
                ans=max(ans,dp(next(i),i));
            }
        }
        printf("%d
",ans);
    }
    return 0;
}
----------------------------

超长回文子序列

Codeforces 335 B-Palindrome

----------------------------

题目给出的n范围较大(5*10^5),要求输出长度为100的回文子序列,若不存在则输出最长的。

注意到隐含条件,序列由小写字母构成,当n>=2600时,必然有某个字母的数量超过100(鸽笼原理),输出100个该字母即可。

注意输出dp方案的思路。

此题也可以用纯dp优化解决。

见题解:    http://codeforces.com/blog/entry/8538

代码:        http://codeforces.com/contest/335/submission/4221965

----------------------------

const int maxs=55555;
const int maxn=3333;
char s[maxs];
int n;
int cnt[27];
char mc;
char ans[maxs];
int t;
int f[maxn][maxn];
int dp(int l,int r){
    if (l>r) return 0;
    if (l==r) return 1;
    //if (l+1==r) return 1+(s[l]==s[r]);
    if (f[l][r]!=-1) return f[l][r];
    if (s[l]==s[r]) return f[l][r]=dp(l+1,r-1)+2;
    return f[l][r]=max(dp(l,r-1),dp(l+1,r));
}
void trace(int l,int r){
    if (l>r) return;
    if (l==r) ans[t++]=s[l];
	else if (s[l]==s[r]){
		ans[t++]=s[l];
		trace(l+1,r-1);
		ans[t++]=s[r];
	}
	else{
		if (f[l+1][r]<f[l][r-1]) trace(l,r-1);
		else trace(l+1,r);
	}
}

int main()
{
    cin>>(s+1);
    n=strlen(s+1);
    memset(cnt,0,sizeof(cnt));
    if (n>=2600){
        for (int i=1;i<=n;i++){
            cnt[s[i]-'a']++;
        }
        for (int i=0;i<26;i++){
            if (cnt[i]>=100){
                mc=i+'a';
                break;
            }
        }
        for (int i=0;i<100;i++){
            ans[i]=mc;
        }
        ans[100]=0;
        cout<<ans<<endl;
    }
    else{
        memset(f,-1,sizeof(f));
        dp(1,n);
        t=0;
        trace(1,n);
        ans[t]=0;
        //cerr<<"nice"<<endl;
        if (t<=100) cout<<ans<<endl;
        else{
            //cout<<"boy next door"<<endl;
            for (int i=0;i<50;i++) cout<<ans[i];
            for (int i=t-50;i<t;i++) cout<<ans[i];
            cout<<endl;
        }
    }
    return 0;
}

----------------------------

回文子序列的数量

HDU 4632 Palindrome subsequence

----------------------------

此题要求输出回文子序列的方案数

当s[i]==s[j]时 区间[i,j]的方案数为回文ij + 区间[i+1,j-1] 的方案数

加上区间[i+1,j] [i,j-1]的方案数,减去区间[i+1,j-1]的方案数(容斥)

----------------------------

const int maxn=1111;
int f[maxn][maxn];
char s[maxn];

int main()
{
    int T,n;
    int cas=0;
    scanf("%d",&T);
    while (T--)
    {
        scanf("%s",(s+1));
        n=strlen(s+1);
        clr(f,0);
        for (int i=1;i<=n;i++)
        {
            f[i][i]=1;
            if (s[i]==s[i+1]&&i+1<=n) f[i][i+1]=3;
            else if (i+1<=n) f[i][i+1]=2;
        }
        for (int k=2;k<n;k++)
        {
            for (int i=1;i+k<=n;i++)
            {
                int j=i+k;
                if (s[i]==s[j]) f[i][j]=f[i+1][j-1]+1;
                f[i][j]+=f[i+1][j]+f[i][j-1]-f[i+1][j-1];
                f[i][j]=(f[i][j]%MOD+MOD)%MOD;
            }
        }
        printf("Case %d: %d
",++cas,f[1][n]);
    }
    return 0;
}
----------------------------

----------------------------

公共子序列

----------------------------

最长公共子序列

HDU 1159 Common Subsequence

----------------------------

这应该是最裸的LCS了,直接dp即可

f[ L ][ R ] 表示第一个串的前L个数与第二个穿的前R个数的最长公共子序列的长度。

f[ L ][ R ] = f[ L-1 ][ R-1 ] +1   ( S[ L ]==S[ R ] )

f[ L ][ R ] = max( f[ L-1 ][ R ] , f[ L ][ R-1 ] )

----------------------------

代码略

----------------------------

三个串的最长公共子序列

USTC 1301 Common sub-sequence

Topcoder SRM 298 DIV 1 1000. CountingCommonSubsequences

----------------------------

https://apps.topcoder.com/forums/?module=Thread&threadID=510443&start=0&mc=5#537441

听说这是很sb的写法。

以下是岛娘的写法。(完全看不懂 待整理)

----------------------------

#define p1 p1[c]
#define p2 p2[c]
#define p3 p3[c]
#define u dp[i1][i2][i3]
const int N = 51;
LL dp[N][N][N];
int p1[26][N], p2[26][N], p3[26][N];

class CountingCommonSubsequences
{
public:
    long long countCommonSubsequences(string s1, string s2, string s3){
        int n1 = SZ(s1), n2 = SZ(s2), n3 = SZ(s3);
        RST(dp);
        REP(c, 26){
            p1[n1] = n1, p2[n2] = n2, p3[n3] = n3;
            DWN(i1, n1, 0) p1[i1] = s1[i1] == 'a' + c ? i1 : p1[i1+1];
            DWN(i2, n2, 0) p2[i2] = s2[i2] == 'a' + c ? i2 : p2[i2+1];
            DWN(i3, n3, 0) p3[i3] = s3[i3] == 'a' + c ? i3 : p3[i3+1];
            dp[p1[0]][p2[0]][p3[0]] = 1;
        }
        LL res = 0;
        REP_3(i1, i2, i3, n1, n2, n3) if (u){
            res += u;
            REP(c, 26) dp[p1[i1+1]][p2[i2+1]][p3[i3+1]] += u;
        }
        return res;
    }
};

----------------------------

公共递增子序列

----------------------------

----------------------------

----------------------------

----------------------------

递增子序列

----------------------------

最长递增子序列

暂时缺题

----------------------------

----------------------------

----------------------------

递增子序列数

HDU 1423 Greatest Common Increasing Subsequence

----------------------------

----------------------------

----------------------------

最长条件子序列

----------------------------

最长差值子序列

HDU 3530 Subsequence

----------------------------

给出一个正整数组成的序列,求他的最大值与最小值差不小于m不大于k的最长子序列。

----------------------------

代码待调整

const int maxn=110000;
int a[maxn];
typedef pair<int,int> PII;
deque<PII>q1,q2;
int main()
{
    int n,m,k;
    int ans,now;
    while (~scanf("%d%d%d",&n,&m,&k))
    {
        ans=0;
        now=0;
        q1.clear();
        q2.clear();
        REP(i,n)
        {
            scanf("%d",&a[i]);
            while (!q1.empty()&&q1.back().first<=a[i]) q1.pop_back();
            q1.push_back(mp(a[i],i));
            while (!q2.empty()&&q2.back().first>=a[i]) q2.pop_back();
            q2.push_back(mp(a[i],i));
            while (!q1.empty()&&!q2.empty()&&q1.front().first-q2.front().first>k)
            {
                if (q1.front().second<q2.front().second)
                {
                    now=q1.front().second+1;
                    q1.pop_front();
                }
                else
                {
                    now=q2.front().second+1;
                    q2.pop_front();
                }
            }
            if (!q1.empty()&&!q2.empty()&&q1.front().first-q2.front().first>=m)
                ans=max(ans,i-now+1);
        }
        printf("%d
",ans);
    }
    return 0;
}


----------------------------

----------------------------

原文地址:https://www.cnblogs.com/cyendra/p/3681577.html