[FJOI2016] all common subsequence problems (DP + Sequential automata + high precision)

Posted by sentient0169 on Mon, 10 Jan 2022 21:01:36 +0100

Rogue portal

Main idea of the title:

Find the number of common subsequences of two strings

Problem solving ideas

Knowledge points:

Sequential automata
According to wjn boss, this is the simplest automata algorithm you have ever learned
I have to say that this automaton is really not difficult, but the questions related to it confuse people
Its core is a Next array
Next[x][c] indicates the position of the first character c after the x position of the current string
The idea of construction is to construct from back to front

void build(int p)
{
	for(int i=n-1;i>=0;i--)
	{
		for(int c=0;c<=52;c++)
		Next[i][c]=Next[i+1][c];
		int ch=turn(s[i+1]);
		Next[i][ch]=i+1;
	}
}

It can be found that sequential automata constitutes a DAG (directed acyclic graph)
And each path from the starting point to the midpoint of DAG corresponds to a subsequence, and these subsequences are essentially different
Return to the problem and construct sequential automata for two strings respectively
In fact, it is to count the number of paths in the common part of two DAG s. DP can be used
Let f[x][y] represent the number of schemes at the x position of the first string and the Y position of the second string
Because the direction of the sequential automata is backward, it is easier for us to reverse, so the current subsequence is after the current position. After all, the exchange order does not change the subsequence
be f [ x ] [ y ] = ∑ f [ N e x t [ x ] [ c ] ] [ N e x t [ y ] [ c ] ] f[x][y]=\sum f[Next[x][c]][Next[y][c]] f[x][y]=∑f[Next[x][c]][Next[y][c]]
And meet N e x t [ x ] [ c ] ≠ 0    a n d    N e x t [ y ] [ c ] ≠ 0 Next[x][c]\neq0\; and\;Next[y][c]\neq0 Next[x][c]​=0andNext[y][c]​=0
However, this topic is not finished here, because it will explode long long
But if you use ordinary high precision, it will explode space
So we need

Pressure level high precision

That is to change the traditional high-precision one digit position to one n digit position
However, n cannot be too large or too small. Take 8 ~ 9 as the best, and add it normally
Finally, pay attention to the output, because each n-digit carry may be less than N, so it needs to be supplemented with 0
ps: it's more convenient to write with memory search

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 3017;
const int mod=1e9;
int Next[2][N][53],n[2];
char s[2][N];
struct node
{
	int tot;
	int s[20];
	void clear()
	{
		for(int i=0;i<20;i++) s[i]=0;
		s[0]=1;
		tot=0;
	}
	void put()
	{
		printf("%lld",s[tot]);
		for(int i=tot-1;i>=0;i--)
		printf("%09lld",s[i]);
		printf("\n");
	}
	void add(const node x)
	{
		int len=max(tot,x.tot);
		for(int i=0;i<=len;i++)
		{
			s[i]+=x.s[i];
			if(s[i]>=mod)
			{
				s[i+1]+=s[i]/mod;
				s[i]%=mod;
			}
		}
		tot=len;
		if(s[len+1]) tot++;
	}
}dp[N][N];
int turn(char ch)
{
	if(ch>='A'&&ch<='Z') return ch-'A';
	else return ch-'a'+26;
}
char Return(int ch)
{
	if(ch>=0&&ch<=25) return ch+'A';
	else return ch+'a'-26; 
}
void build(int p)
{
	for(int i=n[p]-1;i>=0;i--)
	{
		for(int c=0;c<=52;c++)
		Next[p][i][c]=Next[p][i+1][c];
		int ch=turn(s[p][i+1]);
		Next[p][i][ch]=i+1;
	}
}
int k;
char str[N];
int top=0;
bool vis[N][N];
void Print(int x,int y)
{
	printf("%s\n",str+1);
	for(int i=0;i<=51;i++)
	{
		if(!Next[0][x][i]||!Next[1][y][i]) continue;
		str[++top]=Return(i);
		Print(Next[0][x][i],Next[1][y][i]);
		str[top--]=' ';
	}
}
void Getans(int x,int y)
{
	if(vis[x][y]) return;
	vis[x][y]=1;
	dp[x][y].clear();
	for(int i=0;i<=51;i++)
	{
		if(!Next[0][x][i]||!Next[1][y][i]) continue;
		Getans(Next[0][x][i],Next[1][y][i]);
		dp[x][y].add(dp[Next[0][x][i]][Next[1][y][i]]);
	}	
}
int main()
{
	scanf("%d%d",&n[0],&n[1]);
	scanf("%s%s",s[0]+1,s[1]+1);
	build(0);
	build(1);
	scanf("%d",&k);
	if(k==1) Print(0,0);
	Getans(0,0);
	dp[0][0].put();
	return 0;
}

Reference blog: Point me