Don't Be a Subsequence(DP)

                                     Don't Be a Subsequence

                                                                  时间限制: 1 Sec  内存限制: 128 MB
                                                                                  提交: 257  解决: 53
                                                                   [提交] [状态] [讨论版] [命题人:admin]

题目描述

A subsequence of a string S is a string that can be obtained by deleting zero or more characters from S without changing the order of the remaining characters. For example, arc, artistic and (an empty string) are all subsequences of artistic; abc and ci are not.
You are given a string A consisting of lowercase English letters. Find the shortest string among the strings consisting of lowercase English letters that are not subsequences of A. If there are more than one such string, find the lexicographically smallest one among them.

Constraints
1≤|A|≤2×105
A consists of lowercase English letters.

 

输入

Input is given from Standard Input in the following format:
A

 

输出

Print the lexicographically smallest string among the shortest strings consisting of lowercase English letters that are not subsequences of A.

 

样例输入

atcoderregularcontest

样例输出

b

提示

The string atcoderregularcontest contains a as a subsequence, but not b.

                                                                                          [提交]      [状态]

题意:给你一个字符串,问不是这个字符串的最短字典序最小子序列是什么。如果一个字符串t是字符串s的子串,那么t可以由s通过删除0个或多个字符得到。

关键在于发掘出【最短的非子序列字符串】具有最优子结构,定义f(s)为字符串s的最短的非子序列字符串长度,假设最短的非子序列字符串为t,当t的第一个字母是c(任意字母)时,只有两种情况:

①s中无c,f(s)=1最短。

②对于s中最左边位置p的c,f(s)=f(s.suffix(p+1))+1,最后这个+1就是c。

很熟悉对吗?①是终止条件,②是状态转移,满足最优子结构性质,当确定第一个字母后,剩余部分可以转化为计算完毕的子问题

那么正式定义状态转移方程,令f[i]表示字符串的后缀i的最短非子序列字符串的长度,next[i][j]表示从位置i开始第一个字母j出现的位置。

状态转移方程:f[i]=min(f[next[i][j]+1])+1,0<=j<26。

最后要求字典序最小,逆序输出即可,怎么来的怎么回去。


#include<bits/stdc++.h>

using namespace std;

char str[200005];

int dp[200005];

int Next[200005][30];

int main()

{

	scanf("%s",str);

	int len=strlen(str);

	for(int i=0;i<26;i++)//以len结尾的i出现的位置 

		Next[len][i]=len;

		

	for(int i=len-1;i>=0;i--)

		for(int j=0;j<26;j++)//i开头的子串中 j+'a'第一次出现的位置 

			Next[i][j]=((str[i]==(j+'a'))?i:Next[i+1][j]);

	

	dp[len]=1;

	for(int i=len-1;i>=0;i--)

	{

		dp[i] = dp[Next[i][0]+1]+1;

		for(int j=1;j<26;j++)

			dp[i] = min (dp[i], dp[Next[i][j]+1]+1);

	}

	

	int flag=0;

	while(dp[flag])

	{

		for(int i=0;i<26;i++)

		{

			if(dp[flag]==dp[Next[flag][i]+1]+1)

			{

				printf("%c",'a'+i);

				flag=Next[flag][i]+1;

				break;

			}

		}

	}

	printf("\n");

	return 0;

 

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值