2019.10.25字符串——zr
题意:
给你两个字符串,由01组成;求他们两个的最短公共非子序列,要求字典序最小;
非公共子序列:都不是这两个字符串的子序列;
本人只会暴力啊,二进制枚举稳拿15分;
然而这道题其实是一个最短路题;
题解:
贪心考虑从前往后s1……si,维护一个j表示当前字符串已经匹配到t1……tj,
贪心考虑tj'=si+1的j'匹配;
要求字典序最小,实际上我们求得就是一个最短路;
从中止状态反向遍历,就可以记录哪些点在起点到终点的最短路上。DP的状态就是最短路;
再从开始点开始,哪个状态在最短路上,就输出;
时间复杂度(n2);
我觉得吧,其实就是在这两个字符串上找到最短的公共子串(感性理解,实际上并不是这个意思),我们要匹配到n+1,m+1,一个串结束了,并不代表状态的结束;
因为当前状态+0/1可能还是较长串的子序列;
当我们匹配到n+1,m+1的时候就是最短非公共子序列;
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 using namespace std; 5 const int maxn=4010; 6 int n,m; 7 char s[maxn],t[maxn]; 8 9 int nxt_s[maxn][2],nxt_t[maxn][2]; 10 11 int dp[maxn][maxn]; 12 13 int on_road[maxn][maxn]; 14 15 int main() 16 { 17 scanf("%d%d",&n,&m); 18 scanf("%s%s",s+1,t+1); 19 nxt_s[n+1][0]=nxt_s[n+1][1]=n+1; 20 for(int i=n;i>=1;i--) 21 { 22 int c=s[i]-'0'; 23 nxt_s[i][c]=i; 24 nxt_s[i][c^1]=nxt_s[i+1][c^1]; 25 } 26 nxt_t[m+1][0]=nxt_t[m+1][1]=m+1; 27 for(int i=m;i>=1;i--) 28 { 29 int c=t[i]-'0'; 30 nxt_t[i][c]=i; 31 nxt_t[i][c^1]=nxt_t[i+1][c^1]; 32 } 33 34 memset(dp,0x3f,sizeof(dp)); 35 dp[0][0]=0; 36 for(int i=0;i<=n+1;i++) 37 { 38 for(int j=0;j<=m+1;j++) 39 { 40 for(int c=0;c<2;c++) 41 { 42 int x=i<=n?nxt_s[i+1][c]:i; 43 int y=j<=m?nxt_t[j+1][c]:j; 44 if(dp[x][y]>dp[i][j]+1) 45 { 46 dp[x][y]=dp[i][j]+1; 47 } 48 } 49 } 50 } 51 52 on_road[n+1][m+1]=1; 53 for(int i=n+1;i>=0;i--) 54 { 55 for(int j=m+1;j>=0;j--) 56 { 57 for(int c=0;c<2;c++) 58 { 59 int x=i<=n?nxt_s[i+1][c]:i; 60 int y=j<=m?nxt_t[j+1][c]:j; 61 if(dp[x][y]==dp[i][j]+1&&on_road[x][y]) 62 { 63 on_road[i][j]=1; 64 } 65 } 66 } 67 } 68 69 int i=0,j=0; 70 71 while(i<=n||j<=m) 72 { 73 for(int c=0;c<2;c++) 74 { 75 int x=(i<=n?nxt_s[i+1][c]:i); 76 int y=(j<=m?nxt_t[j+1][c]:j); 77 if(dp[x][y]==dp[i][j]+1&&on_road[x][y]) 78 { 79 putchar(c+'0'); 80 i=x;j=y; 81 break; 82 } 83 } 84 } 85 86 return 0; 87 }