luogu4608 [FJOI2016]所有公共子序列问题

题目描述:

luogu

loj

题解:

序列自动机(?)+高精+普及dp。

这个是猫老师的序列自动机(字符串从1开始):

void sol(char *s,int n,int t[N][53])
{
    memset(t[n],-1,sizeof(t[n]));
    for(int i=n-1;i>=0;i--)
    {
        memcpy(t[i],t[i+1],sizeof(t[i]));
        t[i][ci(s[i+1])] = i+1;
    }
}

序列自动机

$t$数组记录在位置$i$的后面加字符$j$能到达的最左边的位置。

对于这道题,直接对两个串构造序列自动机,然后爆搜即可。

$k=1$没啥好说的,$k=0$高精压18位。

时间复杂度$O(n^2)$。

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N = 3020;
const ll M = 1000000000000000000ll;
struct bignum
{
    ll a[10];//8
    int len;
    void operator += (const bignum&x)
    {
        int mx = max(len,x.len);
        for(int i=0;i<mx;i++)
        {
            a[i]+=x.a[i];
            if(a[i]>=M)a[i+1]+=a[i]/M,a[i]%=M;
        }
        while(a[len])len++;
    }
    void operator += (const ll&x)
    {
        a[0]+=x;
        for(int i=0;a[i]>=M;i++)a[i+1]+=a[i]/M,a[i]%=M;
        while(a[len])len++;
    }
    void write()
    {
        if(!len){puts("0");return ;}
        printf("%lld",a[len-1]);
        for(int i=len-2;i>=0;i--)
            printf("%018lld",a[i]);
        puts("");
    }
}dp[2500050];
int n,m,k,t1[N][53],t2[N][53],id[N][N],tot=0,ans=0;
char s1[N],s2[N];
int ci(char ch)
{
    if(ch>=‘A‘&&ch<=‘Z‘)return ch-‘A‘;
    return ch-‘a‘+26;
}
char ic(int i)
{
    if(i<26)return ‘A‘+i;
    return ‘a‘+i-26;
}
void sol(char *s,int n,int t[N][53])
{
    memset(t[n],-1,sizeof(t[n]));
    for(int i=n-1;i>=0;i--)
    {
        memcpy(t[i],t[i+1],sizeof(t[i]));
        t[i][ci(s[i+1])] = i+1;
    }
}
int tl;
char now[N];
void dfs1(int a,int b)
{
    for(int i=1;i<=tl;i++)
        putchar(now[i]);
    putchar(‘\n‘);
    ans++;
    for(int i=0;i<52;i++)if(~t1[a][i]&&~t2[b][i])
    {
        now[++tl] = ic(i);
        dfs1(t1[a][i],t2[b][i]);
        tl--;
    }
}
void dfs2(int a,int b)
{
    if(id[a][b])return ;
    id[a][b]=++tot;
    dp[tot]+=1;
    for(int i=0;i<52;i++)if(~t1[a][i]&&~t2[b][i])
    {
        dfs2(t1[a][i],t2[b][i]);
        dp[id[a][b]]+=dp[id[t1[a][i]][t2[b][i]]];
    }
}

int main()
{
//    freopen("tt.in","r",stdin);
    scanf("%d%d%s%s%d",&m,&n,s1+1,s2+1,&k);
    sol(s1,m,t1),sol(s2,n,t2);
    if(k==1){dfs1(0,0);printf("%d\n",ans);return 0;}
    dfs2(0,0);dp[id[0][0]].write();
    return 0;
}

原文地址:https://www.cnblogs.com/LiGuanlin1124/p/11080765.html

时间: 2024-08-05 04:09:35

luogu4608 [FJOI2016]所有公共子序列问题的相关文章

14-高效求最长公共子序列(二维数组存不下)

/*                                   See LCS again时间限制:1000 ms  |  内存限制:65535 KB难度:3 描述 There are A, B two sequences, the number of elements in the sequence is n.m; Each element in the sequence are different and less than 100000. Calculate the length

最长公共子序列的代码实现

关于最长公共子序列(LCS)的相关知识,http://blog.csdn.net/liufeng_king/article/details/8500084 这篇文章讲的比较好,在此暂时不再详说. 以下是我代码实现两种方式:递归+递推: 1 #include <bits/stdc++.h> 2 using namespace std; 3 int A[100]; 4 int B[100]; 5 6 //int B[]={2,3,5,6,9,8,4}; 7 int d[100][100]={0};

NYOJ 36 &amp;&amp;HDU 1159 最长公共子序列(经典)

链接:click here 题意:tip:最长公共子序列也称作最长公共子串(不要求连续),英文缩写为LCS(Longest Common Subsequence).其定义是,一个序列 S ,如果分别是两个或多个已知序列的子序列,且是所有符合此条件序列中最长的,则 S 称为已知序列的最长公共子序列. 输入 第一行给出一个整数N(0<N<100)表示待测数据组数 接下来每组数据两行,分别为待测的两组字符串.每个字符串长度不大于1000. 输出 每组测试数据输出一个整数,表示最长公共子序列长度.每组

poj1159 Palindrome(最长公共子序列)

Palindrome Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 52966   Accepted: 18271 Description A palindrome is a symmetrical string, that is, a string read identically from left to right as well as from right to left. You are to write a

最长公共子序列(LCS)问题

最长公共子串(Longest Common Substirng)和最长公共子序列(Longest Common Subsequence,LCS)的区别为:子串是串的一个连续的部分,子序列则是从不改变序列的顺序,而从序列中去掉任意的元素而获得新的序列:也就是说,子串中字符的位置必须是连续的,子序列则可以不必连续. 1.序列str1和序列str2 ·长度分别为m和n: ·创建1个二维数组L[m.n]: ·初始化L数组内容为0 ·m和n分别从0开始,m++,n++循环: - 如果str1[m] ==

Longest Common Substring(最长公共子序列)

Longest Common Substring Time Limit: 8000/4000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 37 Accepted Submission(s): 28   Problem Description Given two strings, you have to tell the length of the Longest Common Su

最长公共子序列LCS (DP)

题意: 求两个字符串的公共子序列,如"abcd" 与 "becd"的公共子序列是 "bcd" 分析: 设两个字符串为 串s 和串tdp[i][j]:= s1..si和t1...tj对应的LCS长度 那么 dp[i][j] = { 0                     ,   i =0 or j = 0; dp[i-1][j-1] + 1,   i ,j > 0 and si = tj; max(dp[i-1][j] , dp[i][j

最长公共子序列针对小字符集的算法

一般对于两个字符串,长度分别为n和m,其时间复杂度为O(nm). 但是针对小字符集的情况,可以把复杂度降低到O(n^2),其中n为两个字符串较短的长度.这种方法对于两个字符串长度相差很大的情况比O(nm)要优化很多. 就假设所有的字符都是小写字母,这样就符合小字符集的前提了.设较短的字符串为S1,较长的字符串为S2.字符串下标从1开始. S2字符串每个位置右边第一个字符是可以通过O(km)预处理得到的.其中k为小字符集的字符个数,m为较长的那个字符串的长度. 用next[i][j]表示S2[i]

动态规划-最长公共子序列

(1).问题描述:给出2个序列,x是从1到m,y是从1到n,找出x和y的最长公共子序列? x:A B C B D A B y:B D C A B A 则:最长公共子序列长度为4,BDAB BCAB BCBA均为LCS(最长公共子序列): 模型实现图: (2).问题解决 代码实现了最长公共子序列的长度 #include<stdio.h> #define N    10 int LCS(int *a, int count1, int *b, int count2); int LCS(int *a,