程序员进阶之算法练习(九十三)

题目1

题目链接
题目大意:
有3个字符a/b/c,排成一行;
现在可以选择两个字符,交换对应的位置;
上述操作最多只能执行一次,问能否得到abc的顺序。

输入:
第一行,整数𝑡 表示t个样例 𝑡 (1≤𝑡≤6)
每个样例一行,字符串abc的随机排列;

输出:
每个样例一行,如果有解则输出YES,无解则输出NO;

Examples
input
6
abc
acb
bac
bca
cab
cba

output
YES
YES
YES
NO
NO
YES

题目解析:
将字符串与abc进行匹配,计算得到一共有x个位置的字符匹配;
如果x=3,则全部字符都匹配了,不需要操作;
如果x=1,则表示有2个字符不匹配,那么交换这两个字符;
其他情况则直接输出NO;

class Solution {
    static const int N = 201010;
    int a[N];
public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            char s[10];
            cin >> s;
            int cnt = 0;
            for (int i = 0; i < 3; ++i) if (s[i] == 'a' + i) ++cnt;
            cout << ((cnt == 1 || cnt == 3) ? "YES":"NO") << endl;
        }
    }
}
ac;

题目2

题目链接
题目大意:
给出一个字符串s,现在可以进行以下操作:
1、将某个子串AB,替换成子串BC;
2、将某个子串BA,替换成子串CB;

现在想知道,最多可以对字符串进行多少次操作。

输入:
第一行,整数𝑡 表示t个样例 𝑡 (1≤𝑡≤10000)
每个样例一行,字符串𝑠 ,只有字符A和B (1≤|𝑠|≤2⋅1e5)

输出:
每个样例一行,输出可以最多可以执行的操作数。

Examples
input
8
ABBA
ABA
BAABA
ABB
AAAAAAB
BABA
B
AAA

output
2
1
3
1
6
2
0
0

题目解析:
假设原来字符串是xxxAByyy,进行一次操作1之后,会变成xxxBCyyy;
容易知道,字符C会成为阻断,yyy无法与C形成搭配,但是xxxB仍然可能会产生操作1,比如说AAAB这样的字符串就可以连续执行操作1;
同理,BAAA可以连续执行操作2;

那么将连续的A聚合起来,题目的要求,就变成如何分配B给连续A,使得最终的结果最大;
对于 ABABABA的这样字符,那么从中选择一个最小的A即可。
但是对于BABA、ABBA这样的字符呢?

为了方便计算,我们可以用字符B来分割原字符串。
如果遇到ABBA这样的字符,我们假设在BB中间插入一个A(0)的字符,那么整个算法就完善起来:
整个字符串都可以抽象成这样的的字符组合:Ax B Ax B Ax ..... (Ax表示有连续x个字符A)
比如说BAABA就可以表示为 [A0,B,A2,B,A1],容易知道最终A0是最小,那么结果就是0+2+1-0=3;

class Solution {
    static const int N = 201010;
    string s;
public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            cin >> s;
            int n = s.length();
            int cur = 0;
            vector<int> vec;
            for (int i = 0; i < n; ++i) {
                if (s[i] == 'B') {
                    vec.push_back(cur);
                    cur = 0;
                }
                else {
                    ++cur;
                }
            }
            if (cur != 0 || s[n - 1] == 'B') {
                vec.push_back(cur);
            }
            sort(vec.begin(), vec.end());
            int ans = 0;
            for (int i = 0; i < vec.size(); ++i) {
                ans += vec[i];
            }
            ans -= vec[0];
            cout << ans << endl;
        }
    }
}

题目3

题目链接
题目大意:
Alice和Bob在玩游戏。
初始有n个数字1,每次可以选择2个或者以上相同的数字,将这些数字移除,然后添加这些数字的和;
比如说[1, 1, 1, 1],可以选择2个1合并,得到[2, 1, 1];
现在Alice和Bob轮流进行操作,Alice先手;
如果遇到没有2个相同的数字,那么该轮选手获胜。

输入整数n,表示有n个数字1;
输出Alice或者Bob,表示获胜者;

输入:
第一行,整数𝑡 表示t个样例 𝑡 (1≤𝑡≤99)
每个样例一行,整数𝑛 (2≤𝑛≤200)

输出:
每个样例一行,输出获胜者。

Examples
input
2
3
6

output
Bob
Alice

题目解析:

先从小样例入手:
n=2,[1, 1] = B
n=3,1,1,1 = B
n=4,1,1,1,1 = B
n=5,1,1,1,1,1 = 1,1 + 3 = A
n=6,1,1,1,1,1,1 = 1,1 + 4 = A
...
这里比较容易得到一个必胜策略,只需要拆分为 [1,1] + (n-2) = A,并且n-2比2大即可。

class Solution {
    static const int N = 201010;
    int a[N];
public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            int n;
            cin >> n;
            cout << (n > 4 ? "Alice" : "Bob") << endl;
        }
    }
}
ac;

题目4

题目链接
题目大意:
某个数组被定义为beautiful,只要满足:
将数组前面连续的一段(长度可以是0)切下来,拼接到数组最后面,数组最终是非递减的,那么数组是beautiful。

现在有一个数组,初始化状态为空;
依次给出n个整数,如果某个整数添加到数组末尾后数组是beautiful,那么该整数必须添加到数组末尾,否则放弃;
问最终由有哪些数字会添加到数组中。

输入:
第一行,整数𝑡 表示t个样例 𝑡 (1≤𝑡≤10000)
每个样例2行
第一行,整数𝑛 (1≤𝑛≤2e5)
第二行,n个整数𝑎1,𝑎2,…𝑎𝑛 (0≤𝑎𝑖≤1e9 )

输出:
每个样例一行,由01组成长度为n的字符串;第i个字符为1表示第i个字符会被添加到数组,为0则表示不会;

Examples
input
3
9
3 7 7 9 2 4 6 3 4
5
1 1 1 1 1
5
3 2 1 2 3

output
111110010
11111
11011

题目解析:
按照题目的要求,在过程中并没有决策的空间,那么关键点就是如何快速得到这个结果。

1、当a[i+1] >= a[i],继续保持;
2、当a[i+1] < a[i],首次出现就要进行分割,a[i+1]要放在数组末尾,如果a[1] >= a[i],那么a[i]可行;
接下来要满足,所有数字大于等于a[i]并且小于等于a[1];

这里犯了一次低级错误 cur = a[i]写成了cur = ans[i];
导致出现几次Wrong Answer,后面名字要有差异。

class Solution {
    static const int N = 201010;
    int a[N], ans[N];
public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            int n;
            cin >> n;
            bool rev = 0;
            int cur = 0;
            memset(ans, 0, sizeof(ans));
            for (int i = 0; i < n; ++i) {
                cin >> a[i];
                if (i == 0) {
                    ans[i] = 1;
                    cur = a[i];
                }
                else {
                    if (rev) {
                        if (a[i] >= cur && a[i] <= a[0]) {
                            cur = a[i];
                            ans[i] = 1;
                        }
                    }
                    else {
                        //  0 4 15 18 4 10 12 8 13 8 15 0 14 12 10 12 10 14 13
                        if (a[i] >= cur) {
                            ans[i] = 1;
                            cur = a[i];
                        }
                        else if (i == 1 || a[0] >= a[i]){
                            cur = a[i];
                            rev = 1;
                            ans[i] = 1;
                        }
                    }
                }
            }
            for (int i = 0; i < n; ++i) putchar('0' + ans[i]);
            puts("");
        }
    }
}

题目5

题目链接
题目大意:
有长度为n的字符串s,由字符A/B/C/D/E组成;
现在将字符串按照下述规则转成数字:
1、A、B、C、D、E分别代表数字1、10、100、1000、10000;
2、如果某个字符,右边的位置存在一个字符比当前字符更大,则添加负号;(比如说AB,A的右边存在B比当前字符A大,那么A代表-1)
将字符串每个位置数字累加,得到字符串的和;
比如说:
ABB = -1 + 10 + 10 = 19;
BBA = 10 + 10 + 1 = 21;

现在可以修改字符串s中的一个字符,替换为A~E中的任意一个字符;
问,字符串的和最大为多少?

输入:
第一行,整数𝑡 表示t个样例 𝑡 (1≤𝑡≤10000)
每个样例一行,字符串𝑠(1≤|𝑠|≤2⋅105)

输出:
每个样例一行,输出修改后最大的字符串和;

Examples
input
4
DAAABDCA
AB
ABCDEEDCBA
DDDDAAADDABECD

output
11088
10010
31000
15886

题目解析:
还是从简单开始思考。
只有单个字母时,直接选择替换为E,收益为E与当前字母的差距;
当有两个字母时,就需要考虑特殊情况,正常AB这样的组合,还是会选择替换成EB;但是当BA这样的组合时,继续选A就会导致B变成负数,此时除了正收益,还有额外的负收益;
那么就需要统一计算,负收益也比较容易计算:替换后,所在位置前,原来ABCD字母价值为正的部分;(注意,如果原来就为负,没有负收益)
这样从左到右枚举整个数组即可得到最优解。

但是,自己还是犯了一个错误:主观判断,无法证明。
在分析样例的时候,还是太过急,从两个字母直接推出来最优解,情况还是不够丰富。
因为修改字母除了修改为最大,还可以修改为较小值。
这里既然要枚举每个位置的值,是不是也可以考虑枚举每个位置修改为A/B/C/D/E的值?
这样可以解决DDDDDDDDDDDDDDE这样的case,我们可以修改为DDDDDDDDDDDDDDD。

所以,更严谨的做法,我们应该枚举每一个位置分别对应修改为A/B/C/D/E的情况,但是这样的复杂度是O(N^2),明显超出题目限制;
但是分析其中冗余的部分,由贪心思想我们可以发现,假如一个字符D要修改,要么就是第一个D,要么是最后一个D;
为什么呢?
由最开始的替换为E的思路,这是从博取正收益的角度去出发,在这种情况下,假设要修改的是字符C,那么越往左的字符C,整体收益是越大的;
假如是我们从减少负收益的角度去出发,假设我们要修改是字符E,那么越往右的字符E,整体收益是越大的;
所以我们只要最开始出现和最后出现ABCDE位置,一共10个位置,每个位置再枚举修改为A/B/C/D/E的最大收益即可。

class Solution {
    static const int N = 201010;
    lld a[N];
    char s[N];
    lld val[5] = {1, 10, 100, 1000, 10000};
    
    lld getSum(int n) {
        lld ret = 0;
        char cur = 0;
        for (int i = n; i > 0; --i) {
            a[i] = val[s[i] - 'A'];
            if (s[i] < cur) a[i] *= -1;
            cur = max(cur, s[i]);
            ret += a[i];
        }
        return ret;
    }
    
public:
    void solve() {
        int t;
        cin >> t;
        while (t--) {
            scanf("%s", s+1);
            int n = strlen(s+1);
            int posFirst[5] = {0}, posLast[5] = {0};
            for (int i = 1; i <= n; ++i) {
                int idx = s[i] - 'A';
                if (!posFirst[idx]) posFirst[idx] = i;
                posLast[idx] = i;
            }
            lld ans = -0xfffffffffff;
            for (int i = 0; i < 5; ++i) {
                for (int j = 0; j < 5; ++j) {
                    if (!posFirst[i]) continue;;
                    char c = 'A' + j;
                    char tmp = s[posFirst[i]];
                    s[posFirst[i]] = c;
                    ans = max(ans, getSum(n));
                    s[posFirst[i]] = tmp;
                }
            }
            
            for (int i = 0; i < 5; ++i) {
                for (int j = 0; j < 5; ++j) {
                    if (!posLast[i]) continue;;
                    char c = 'A' + j;
                    char tmp = s[posLast[i]];
                    s[posLast[i]] = c;
                    ans = max(ans, getSum(n));
                    s[posLast[i]] = tmp;
                }
            }
            
            cout << ans << endl;
        }
    }
}
ac;
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容