zoukankan      html  css  js  c++  java
  • @atcoder


    @description@

    给定一个仅由 A, B, C 组成的字符串 S。

    求 S 的一个最长子序列(不一定连续),满足:
    (1)A, B, C 出现了相同次数。
    (2)子序列中相邻字符不相同。

    输出该子序列。

    Constraints
    1≤|S|≤10^6
    S 仅由 A, B, C 组成。

    Input
    输入仅给定单个字符串 S。

    Output
    输出任意一个满足条件的最长子序列。

    Sample Input 1
    ABBCBCAB
    Sample Output 1
    ACBCAB

    Sample Input 2
    ABABABABACACACAC
    Sample Output 2
    BABCAC

    @solution@

    不难想到,在原串 S 中相同的相邻字符可以先取掉,即一个去重操作。

    不妨假设 cntA <= cntB <= cntC,即 A 的数量是最少的。
    因为我们的最长子序列最大也只能到 3*cntA,而且可能会更少。
    我们希望删尽量少的 A,来使得三者相同。

    用字符 A 将原序列分割成若干段,每一段肯定是 BCBCB... 这样 B, C 交替出现。
    我们先想办法让 cntB = cntC。假如一个 C 的两边不相同,去掉它过后不会影响结果,我们就直接删掉它。
    如果还是 cntB < cntC,此时每一段要么是 ABCB...CBA,这种类型 B 只能比 C 多 1 个,不可再更改;要么是 ACA,即两个 A 中间夹一个 C,我们可以同时去掉一个 A 与一个 C。
    可以发现这些 A 是必须去掉的,不然 cntB 永远无法等于 cntC。

    现在就有 cntA <= cntB = cntC,我们来想办法让 cntA = cntB = cntC。
    可以每次删一个 "BC" 或者 "CB" 模样的子串,但是必须要保证删完过后两个 A 不会靠在一起。
    最后如果还有 A,则至少都有 ABCACBA...BCA,即两个 A 中间夹一个 "BC" 或 "CB"。这时 cntA > cntB = cntC。
    所以删的过程一定存在每一刻满足题意。

    注意如果最终三个字符的数量依然不相等,就无解。
    删除什么的可以用链表 O(1) 搞。总复杂度 O(n)。

    @accepted code@

    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int MAXN = 1000000;
    int cnt[3], b[3], c[3];
    void get() {
    	if( cnt[0] >= cnt[1] && cnt[1] >= cnt[2] )
    		c[b[0] = 2] = 0, c[b[1] = 1] = 1, c[b[2] = 0] = 2;
    	else if( cnt[0] >= cnt[2] && cnt[2] >= cnt[1] )
    		c[b[0] = 2] = 0, c[b[2] = 1] = 2, c[b[1] = 0] = 1;
    	else if( cnt[1] >= cnt[0] && cnt[0] >= cnt[2] )
    		c[b[1] = 2] = 1, c[b[0] = 1] = 0, c[b[2] = 0] = 2;
    	else if( cnt[1] >= cnt[2] && cnt[2] >= cnt[0] )
    		c[b[1] = 2] = 1, c[b[2] = 1] = 2, c[b[0] = 0] = 0;
    	else if( cnt[2] >= cnt[0] && cnt[0] >= cnt[1] )
    		c[b[2] = 2] = 2, c[b[0] = 1] = 0, c[b[1] = 0] = 1;
    	else if( cnt[2] >= cnt[1] && cnt[1] >= cnt[0] )
    		c[b[2] = 2] = 2, c[b[1] = 1] = 1, c[b[0] = 0] = 0;
    }
    int lst[MAXN + 5], nxt[MAXN + 5];
    int a[MAXN + 5], n;
    void erase(int x) {
    	lst[nxt[x]] = lst[x], nxt[lst[x]] = nxt[x];
    	cnt[a[x]]--;
    }
    char s[MAXN + 5];
    vector<int>v;
    int main() {
    	scanf("%s", s + 1), n = strlen(s + 1);
    	for(int i=0;i<=n+1;i++)
    		lst[i] = i - 1, nxt[i] = i + 1;
    	a[0] = a[n + 1] = -1;
    	for(int i=1;i<=n;i++) {
    		cnt[a[i] = s[i] - 'A']++;
    		if( a[i] == a[i-1] ) erase(i);
    	}
    	get();
    	for(int i=nxt[0];i!=n+1;i=nxt[i])
    		if( a[i] == c[2] && a[lst[i]] != a[nxt[i]] )
    			v.push_back(i);
    	for(int i=0;i<v.size()&&cnt[c[2]]!=cnt[c[1]];i++)
    		erase(v[i]);
    	v.clear();
    	for(int i=nxt[0];i!=n+1;i=nxt[i])
    		if( a[i] == c[2] && a[lst[i]] == c[0] && a[nxt[i]] == c[0] )
    			v.push_back(i);
    	for(int i=0;i<v.size()&&cnt[c[2]]!=cnt[c[1]];i++)
    		erase(v[i]), erase(nxt[v[i]]);
    	v.clear();
    	for(int i=nxt[0];i!=n+1;i=nxt[i])
    		if( a[i] == c[2] && a[nxt[i]] == c[1] )
    			v.push_back(i);
    	for(int i=0;i<v.size()&&cnt[c[0]]!=cnt[c[1]];i++)
    		if( a[lst[v[i]]] != c[0] || a[nxt[nxt[v[i]]]] != c[0] )
    			erase(v[i]), erase(nxt[v[i]]);
    	v.clear();
    	for(int i=nxt[0];i!=n+1;i=nxt[i])
    		if( a[i] == c[2] && a[lst[i]] == c[1] )
    			v.push_back(i);
    	for(int i=0;i<v.size()&&cnt[c[0]]!=cnt[c[1]];i++)
    		if( a[nxt[v[i]]] != c[0] || a[lst[lst[v[i]]]] != c[0] )
    			erase(v[i]), erase(lst[v[i]]);
    	v.clear();
    	if( cnt[0] == cnt[1] && cnt[0] == cnt[2] ) {
    		for(int i=nxt[0];i!=n+1;i=nxt[i])
    			putchar(a[i] + 'A');
    	}
    }
    

    @details@

    这个肯定比 AGC036D 好想。

    注意有些字符删掉是会互相影响的。有可能前一个删掉,后一个原本可以删,又变成不能删的了。

  • 相关阅读:
    将log4Net记录的日志导入数据库操作指南
    算法40 leetcode 155.最小栈
    c++求以3为底的对数 遇到243 怎么也通不过
    算法36 动规初探&&斐波那契&&尾递归
    算法38 5. 最长回文子串
    算法39 leetcode22. 括号生成
    算法34 堆排序heapsort
    算法37 动规求数组和
    算法35 力扣twosum
    c++ vector size()18446744073709551615 bug
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/11714204.html
Copyright © 2011-2022 走看看