(。・∀・)ノ゙20 年的最后一天了耶!
本蒟蒻又來寫題解了(
才不是沒有找到題解才來寫的呢,But 這次出了些意外沒有 AK 😭是我不配(
過菜了!=== 21-02-25 update ===
感謝評論區老哥提供的測試點,經測試修改成功 AK!
感受到了自己 Debug 能力的不足,新年繼續努力吧~🥳Tips:
全部采用 C++ 求解
代碼部分 ——
純享版不寫注釋,適合
品味;注釋版有一定量的注釋,適合理解;
let me try用於在看懂代碼的前提下,自己順着思路碼一遍使用的提示版。
那么各位,咱們開始吧!
慘案
=== 21-02-25 update ===
試題集
鏈接
概況
7-1 祖傳好運 (15分)
我們首先定義 0 到 9 都是好運數,然后從某個好運數開始,持續在其右邊添加數字,形成新的數字。我們稱一個大於 9 的數字 N 具有祖傳好運,如果它是由某個好運數添加了一個個位數字得到的,並且它能被自己的位數整除。
例如 123 就是一個祖傳好運數。首先因為 1 是一個好運數的老祖宗,添加了 2 以后,形成的 12 能被其位數 2 (即 12 是一個 2 位數)整除,所以 12 是一個祖傳好運數;在 12 后面添加了 3 以后,形成的 123 能被其位數 3 整除,所以 123 是一個祖傳好運數。
本題就請你判斷一個給定的正整數 N 是不是具有祖傳的好運。
輸入格式:
每個輸入包含 1 個測試用例。每個測試用例第 1 行給出正整數 K (≤1000);第 2 行給出 K 個不超過 \(10^{9}\) 的待評測的正整數,注意這些數字都保證沒有多余的前導零。
輸出格式:
對每個待評測的數字,在一行中輸出 Yes
如果它是一個祖傳好運數,如果不是則輸出 No
。
輸入樣例:
5
123 7 43 2333 56160
輸出樣例:
Yes
Yes
No
No
Yes
解析
題意再放送
給你一堆數,如果它從高位到低位、每增加一位時所得到的數字 都 能 整除 它本身的位數,則輸出 Yes,反之 No。
思路
很明顯了,就是模擬題,對於從高位向低位走的操作,使用 std::string
會很舒服。
代碼
純享版
#include <cstdio>
#include <string>
using std::string;
bool judge(int num) {
string s = std::to_string(num);
num = 0;
for (int i = 0; i < s.length(); i++) {
num *= 10;
num += s[i] - '0';
if (num % (i + 1) != 0) {
return false;
}
}
return true;
}
int main() {
int n, num;
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d", &num);
puts(judge(num) ? "Yes" : "No");
}
return 0;
}
注釋版
#include <cstdio>
#include <string>
// 就不用 namespace std,略略略
using std::string;
/**
* 判斷數字是否為好運數的函數
* @param num 那個數
* @return 是或不是好運數
*/
bool judge(int num) {
// int 轉 string
string s = std::to_string(num);
num = 0;
// 從高位往低位遍歷
for (int i = 0; i < s.length(); i++) {
// 按位累加
num *= 10;
num += s[i] - '0';
// i + 1 即為當前數字的位數
if (num % (i + 1) != 0) {
return false;
}
}
return true;
}
int main() {
int n, num;
// IO IO IO
scanf("%d", &n);
for (int i = 0; i < n; i++) {
scanf("%d", &num);
puts(judge(num) ? "Yes" : "No");
}
return 0;
}
let me try
#include <cstdio>
#include <string>
using std::string;
/**
* 判斷數字是否為好運數的函數
* @param num 那個數
* @return 是或不是好運數
*/
bool judge(int num) {
// int 轉 string
// 從高位往低位遍歷
// 字符下標即為當前數字的位數
}
int main() {
// 輸入、判斷、輸出
return 0;
}
7-2 找奇葩 (20分)
在一個長度為 n 的正整數序列中,所有的奇數都出現了偶數次,只有一個奇葩奇數出現了奇數次。你的任務就是找出這個奇葩。
輸入格式:
輸入首先在第一行給出一個正整數 n(≤ \(10^{4}\)),隨后一行給出 n 個滿足題面描述的正整數。每個數值不超過 \(10^{5}\),數字間以空格分隔。
輸出格式:
在一行中輸出那個奇葩數。題目保證這個奇葩是存在的。
輸入樣例:
12
23 16 87 233 87 16 87 233 23 87 233 16
輸出樣例:
233
解析
題意再放送
給你一堆數,奇數偶數都有,但是只有一個奇數很奇葩,只出現奇數次。換言之,其他奇數都出現了偶數次,請你找出那個奇葩。
思路
只需要去處理和統計奇數就行,偶數無視掉。
- 使用 map 全部存起來,再遍歷一次得到答案
- 使用異或位運算,把奇數全部異或,最終的值即為答案
map 是常規思路,而異或這個方法,沒有接觸過幾乎很難想到,其實這道題就是 “落單的數” 這道經典題的小修改版。
異或,相同為 0 不同為 1 ,過程:
[1, 1, 2] -> 0 ^ 1 = 1 -> 1 ^ 1 = 0 -> 00 ^ 10 -> 10 -> 2
代碼
純享版
#include <cstdio>
int main() {
int n, num;
scanf("%d", &n);
int res = 0;
for (int i = 0; i < n; i++) {
scanf("%d", &num);
if (num & 1) {
res ^= num;
}
}
printf("%d\n", res);
return 0;
}
注釋版
#include <cstdio>
int main() {
int n, num;
scanf("%d", &n);
// 使用 0 作為初始值進行所有奇數的異或運算
int res = 0;
for (int i = 0; i < n; i++) {
scanf("%d", &num);
// 如果 num 為奇數,則進行異或,num & 1 等價於 num % 2 == 1
if (num & 1) {
res ^= num;
}
}
// 輸出
printf("%d\n", res);
return 0;
}
let me try
#include <cstdio>
int main() {
// 使用 0 作為初始值
// 輸入
// 進行所有奇數的異或運算
// 輸出
return 0;
}
7-3 舍入 (20分)
不同的編譯器對浮點數的精度有不同的處理方法。常見的一種是“四舍五入”,即考察指定有效位的后一位數字,如果不小於 5,就令有效位最后一位進位,然后舍去后面的尾數;如果小於 5 就直接舍去尾數。另一種叫“截斷”,即不管有效位后面是什么數字,一概直接舍去。還有一種是“四舍六入五成雙”,即當有效位的后一位數字是 5 時,有 3 種情況要考慮:如果 5 后面還有其它非 0 尾數,則進位;如果沒有,則當有效位最后一位是單數時進位,雙數時舍去,即保持最后一位是雙數。
本題就請你寫程序按照要求處理給定浮點數的舍入問題。
輸入格式:
輸入第一行給出兩個不超過 100 的正整數 N 和 D,分別是待處理數字的個數和要求保留的小數點后的有效位數。隨后 N 行,每行給出一個待處理數字的信息,格式如下:
指令符 數字
其中指令符
是表示舍入方法的一位數字,1
表示“四舍五入”,2
表示“截斷”,3
表示“四舍六入五成雙”;數字
是一個總長度不超過 200 位的浮點數,且不以小數點開頭或結尾,即 0.123
不會寫成 .123
,123
也不會寫成 123.
。此外,輸入保證沒有不必要的正負號(例如 -0.0
或 +1
)。
輸出格式:
對每個待處理數字,在一行中輸出根據指令符處理后的結果數字。
輸入樣例:
7 3
1 3.1415926
2 3.1415926
3 3.1415926
3 3.14150
3 3.14250
3 3.14251
1 3.14
輸出樣例:
3.142
3.141
3.142
3.142
3.142
3.143
3.140
解析
題意再放送
給你一堆數字,你要根據指令的要求對其進行精度舍入。
- 四舍五入
- 直接舍棄
- 如果有效位的后一位是5
- 5 后面還有非 0 尾數,進位
- 若沒有尾數,則有效位最后一位是奇數時進位,偶數時舍棄
思路
一個比較糾纏的模擬題,u1s1,和天梯賽的口罩題有的一拼,只能說能提升思維的全面性吧,算法提升約等於 0
反正我是🤮了,考慮前:
坑點:
- 負號處理
- 9.9999999 類似這種牽一發而動全身的數字
- 給你整數,需要補 0 成為浮點數
- ???暫未發現(以下為做出的嘗試,均不是卡點
- 正號處理,去掉或保留
- 前導 0 的處理
處理后:
看到返回結果時就很:
提交了幾次后還不能過時:
是我不配了😤
=== 21-02-25 update ===
感謝評論區老哥@昵稱很長很長真是太好了提供的測試點,經測試修改成功 AC!(舒服了😋)
代碼
求大佬教我怎么改 QAQ (✅已 AC )
#include <cstdio>
#include <string>
#include <algorithm>
using std::string;
/**
* 操作 2,舍棄有效位后面位數的操作
* @param num 原數字
* @param ans 處理值
* @param dot 小數點位置
* @param bit 保留位數
*/
void cmd2(string& num, string& ans, int dot, int bit) {
ans = num.substr(0, dot + bit + 1);
}
/**
* 操作 1,四舍五入
* @param num 原數字
* @param ans 處理值
* @param dot 小數點位置
* @param bit 保留位數
*/
void cmd1(string& num, string& ans, int dot, int bit) {
// 五入
if (num[dot + bit + 1] >= '5') {
int index = dot + bit, carry = 0;
// 模擬進位操作,可處理 9.99999 這種數字
while ((num[index] != '-' && num[index] != '+') && index >= 0) {
if (num[index] == '.') {
ans.push_back('.');
} else {
carry += num[index] - '0' + (index == dot + bit ? 1 : 0);
ans.push_back(carry % 10 + '0');
carry /= 10;
}
index--;
}
// 如果 carry 還有值,則再補上 1
if (carry) {
ans.push_back('1');
}
// 補負號
if (num[0] == '-') {
ans.push_back('-');
}
// 翻轉得到答案
std::reverse(ans.begin(), ans.end());
} else {
// 四舍
cmd2(num, ans, dot, bit);
}
}
/**
* 類似於工廠模式,對指令做相應處理
* @param num 原數字
* @param cmd 指令
* @param bit 保留位數
* @return 答案
*/
string process(string& num, int cmd, int bit) {
// 找到點的位置
int dot = num.find('.');
int len = num.length();
string ans;
// 位數不夠補 0,使得小數點后的位數至少為 bit + 1
// eg: bit = 3, num = "3.1" -> "3.1000"
if (len - dot - 1 < bit + 1) {
num.append(bit + 1 - len + dot + 1, '0');
}
// 進行對應處理
if (cmd == 1) {
cmd1(num, ans, dot, bit);
} else if (cmd == 2) {
cmd2(num, ans, dot, bit);
} else {
// 有效位的后一位進行四舍六入
if (num[dot + bit + 1] < '5') {
cmd2(num, ans, dot, bit);
} else if (num[dot + bit + 1] > '5') {
cmd1(num, ans, dot, bit);
} else {
// 有效位的后一位是 5 時
bool flag = false;
for (int i = dot + bit + 2; i < len; i++) {
if (num[i] != '0') {
flag = true;
break;
}
}
// 5 后面還有非 0 數字
if (flag) {
cmd1(num, ans, dot, bit);
} else {
// 有效位的最后一位是偶數時舍去后面的數,否則進位
if ((num[dot + bit] - '0') % 2 == 0) {
cmd2(num, ans, dot, bit);
} else {
cmd1(num, ans, dot, bit);
}
}
}
}
// ===== bug-fixed on 21-2-25 00:10 contributed by @昵稱很長很長真是太好了 //
// 負數舍位后為 -0.0 時應去掉負號
// 當且僅當整數部分為 0 時
if (ans[0] == '-' && ans[1] == '0') {
bool flag = true;
for (int i = dot + 1; i < ans.length(); i++) {
if (ans[i] > '0') {
flag = false;
break;
}
}
if (flag) {
ans.erase(ans.begin());
}
}
// ===== bug-fixed on 21-2-25 00:10 contributed by @昵稱很長很長真是太好了 //
return ans;
}
int main() {
int n, bit, cmd;
scanf("%d %d", &n, &bit);
char num[205];
for (int i = 0; i < n; i++) {
scanf("%d %s", &cmd, num);
string str{ num };
// 輸入是整數時,補上小數點再處理
if (str.find('.') == string::npos) {
str.push_back('.');
}
puts(process(str, cmd, bit).c_str());
}
return 0;
}
7-4 最近的斐波那契數 (20分)
斐波那契數列 \(F_{n}\) 的定義為:對 n ≥ 0 有 \(F_{n+2}\)=\(F_{n+1}\)+\(F_{n}\),初始值為 \(F_{0}\) = 0 和 \(F_{1}\) = 1。所謂與給定的整數 N 最近的斐波那契數是指與 N 的差之絕對值最小的斐波那契數。
本題就請你為任意給定的整數 N 找出與之最近的斐波那契數。
輸入格式:
輸入在一行中給出一個正整數 N(≤\(10^{8}\))。
輸出格式:
在一行輸出與 N 最近的斐波那契數。如果解不唯一,輸出最小的那個數。
輸入樣例:
305
輸出樣例:
233
樣例解釋
部分斐波那契數列為 { 0, 1, 1, 2, 3, 5, 8, 12, 21, 34, 55, 89, 144, 233, 377, 610, ... }。可見 233 和 377 到 305 的距離都是最小值 72,則應輸出較小的那個解。
解析
題意再放送
給你一個數,找出斐波那契數列里和它最接近的數
思路
我佛了,這道題 2 分鍾秒了,上一道模擬我做了 2 個小時🙃🙃🙃
繼續模擬,按照動態規划的思想,通過保存上一個 fib 數和現在這個 fib 數,最后與所給的數字進行作差比較即可
代碼
純享版
#include <cstdio>
int main() {
int fst = 0, sec = 1, fib = 1;
int n, former = fst;
scanf("%d", &n);
while (fib < n) {
former = fib;
fst = sec;
sec = fib;
fib = fst + sec;
}
printf("%d\n", fib - n >= n - former ? former : fib);
return 0;
}
注釋版
#include <cstdio>
int main() {
// 初始化數列的前三個數
int fst = 0, sec = 1, fib = 1;
int n, former = fst;
scanf("%d", &n);
// 找到數列第一個比 n 大的數
while (fib < n) {
former = fib;
fst = sec;
sec = fib;
fib = fst + sec;
}
// [former, n, fib] -> 看誰離 n 最近(差值的絕對值最小,相同時優先選擇former)
printf("%d\n", fib - n >= n - former ? former : fib);
return 0;
}
let me try
#include <cstdio>
int main() {
// 初始化數列的前三個數
// 輸入
// 找到數列第一個比 n 大的數
// [former, n, fib] -> 看誰離 n 最近(差值的絕對值最小,相同時優先選擇former)
return 0;
}
7-5 子串與子列 (25分)
子串是一個字符串中連續的一部分,而子列是字符串中保持字符順序的一個子集,可以連續也可以不連續。例如給定字符串 atpaaabpabtt
,pabt
是一個子串,而 pat
就是一個子列。
現給定一個字符串 S 和一個子列 P,本題就請你找到 S 中包含 P 的最短子串。若解不唯一,則輸出起點最靠左邊的解。
輸入格式:
輸入在第一行中給出字符串 S,第二行給出 P。S 非空,由不超過 \(10^{4}\) 個小寫英文字母組成;P 保證是 S 的一個非空子列。
輸出格式:
在一行中輸出 S 中包含 P 的最短子串。若解不唯一,則輸出起點最靠左邊的解。
輸入樣例:
atpaaabpabttpcat
pat
輸出樣例:
pabt
解析
題意再放送
給你兩個字符串,S 和 P。要你找出順序包含 P 的 S 的最短子串
思路
第一次做這道題的時候,使用了一次貪心的思想,即:
- 類似 KMP 算法一樣,逐個進行匹配
- 從找到 P 的首字母開始,使用 vector 記錄每個首字母的下標位置
- 直到按順序找到第一個包含 P 的子串(樣例搜索到符合條件的第一個子串:paaabpabt)
- 開始檢查,找到這個子串里最短的符合要求的子串
結果測試點二答案錯誤:
自己再調試了一波,發現了一次貪心匹配是會漏掉一些情況的:
abababababababababaab
baab
答案應該為 baab,程序執行結果卻是 babab,這是由於貪心過程中無法中斷,導致漏掉一些更短的答案
接着我嘗試了兩次貪心,即從前往后和從后往前兩次,代碼幾乎是重復的。成功 AC 時,我開始懷疑貪心是不是正解😂
兩次貪心:
最后本着試試的心態,開始暴力,果然 300ms 的時限成功卡死測試點 2,於是自己進行了優化和剪枝,也成功 AC 了。
暴力:
暴力(優化):
代碼
純享版
暴力(暴力出奇跡)
#include <cstdio>
#include <vector>
#include <string>
using std::string;
using std::vector;
constexpr int MAX_N = 10050;
char str1[MAX_N], str2[MAX_N];
void solve(string& s1, string& s2, string& ans) {
vector<int> st, ed;
int len1 = s1.length(), len2 = s2.length();
for (int i = 0; i < len1; i++) {
if (s1[i] == s2.front()) {
st.push_back(i);
}
if (s1[i] == s2.back()) {
ed.push_back(i);
}
}
for (int i = 0, j = 0; i < st.size() && j < ed.size(); i++) {
while (st[i] + len2 - 1 > ed[j]) {
j++;
}
for (int k = j; k < ed.size(); k++) {
if (!ans.empty() && ed[k] - st[i] + 1 >= ans.length()) {
break;
}
for (int a = st[i], b = 0; a <= ed[k]; a++) {
if (s1[a] == s2[b]) {
b++;
if (b == len2) {
string temp = s1.substr(st[i], ed[k] - st[i] + 1);
if (ans.empty() || ans.length() > temp.length()) {
ans = temp;
}
if (ans.length() == len2) {
return;
}
}
}
}
}
}
}
int main() {
scanf("%s %s", str1, str2);
vector<int> idx;
string s1{ str1 }, s2{ str2 }, ans;
solve(s1, s2, ans);
puts(ans.c_str());
return 0;
}
貪心
#include <cstdio>
#include <vector>
#include <string>
using std::string;
using std::vector;
constexpr int MAX_N = 10050;
char str1[MAX_N], str2[MAX_N];
void findBest(vector<int>& v, string& s1, string& s2, string& ans, int end) {
int len = s2.length();
while (!v.empty()) {
int index = v.back();
v.pop_back();
if (index + len - 1 <= end) {
int j = 0;
for (int i = index; i <= end; i++) {
if (s1[i] == s2[j]) {
j++;
}
}
if (j == len) {
string temp = s1.substr(index, end - index + 1);
if (ans.empty() || temp.length() < ans.length()) {
ans = temp;
return;
}
}
}
}
}
void findBestRev(vector<int>& v, string& s1, string& s2, string& ans, int st) {
int len = s2.length();
while (!v.empty()) {
int end = v.back();
v.pop_back();
if (st + len - 1 <= end) {
int j = 0;
for (int i = st; i <= end; i++) {
if (s1[i] == s2[j]) {
j++;
}
}
if (j == len) {
string temp = s1.substr(st, end - st + 1);
if (ans.empty() || temp.length() < ans.length()) {
ans = temp;
return;
}
}
}
}
}
int main() {
scanf("%s %s", str1, str2);
vector<int> idx;
string s1{ str1 }, s2{ str2 }, ans;
int len1 = s1.length();
int len2 = s2.length();
for (int i = 0, j = 0; i < len1; i++) {
if (str1[i] == str2[0]) {
idx.push_back(i);
}
if (str1[i] == str2[j]) {
j++;
if (j == len2) {
findBest(idx, s1, s2, ans, i);
idx.clear();
j = 0;
}
}
}
for (int i = len1 - 1, j = len2 - 1; i >= 0; i--) {
if (str1[i] == str2[len2 - 1]) {
idx.push_back(i);
}
if (str1[i] == str2[j]) {
j--;
if (j == -1) {
findBestRev(idx, s1, s2, ans, i);
idx.clear();
j = len2 - 1;
}
}
}
puts(ans.c_str());
return 0;
}
注釋版
暴力
#include <cstdio>
#include <vector>
#include <string>
using std::string;
using std::vector;
constexpr int MAX_N = 10050;
char str1[MAX_N], str2[MAX_N];
/**
* 暴力匹配函數
* 通過預處理得到 P 的首尾字符在 S 中的所有下標位置
* 通過首位置與尾位置的相對位置篩選出最合適的位置進行查找
* 找到或找不到都進行一定的剪枝
* @param s1 主串 S
* @param s2 “客”串 P
* @param ans 最短子串
*/
void solve(string& s1, string& s2, string& ans) {
vector<int> st, ed;
int len1 = s1.length(), len2 = s2.length();
// 獲得 s2 的首尾字符在 s1 中的所有位置
for (int i = 0; i < len1; i++) {
if (s1[i] == s2.front()) {
st.push_back(i);
}
if (s1[i] == s2.back()) {
ed.push_back(i);
}
}
// 暴力匹配
for (int i = 0, j = 0; i < st.size() && j < ed.size(); i++) {
// 找到第一個合適的尾位置,即首尾位置確定的子串長度一定是要 >= s2 的長度的
while (st[i] + len2 - 1 > ed[j]) {
j++;
}
// 檢查是否為合法子串
for (int k = j; k < ed.size(); k++) {
// 已有預選答案且當前子串的長度比答案字符串長,剪枝
if (!ans.empty() && ed[k] - st[i] + 1 >= ans.length()) {
break;
}
// 開始匹配,看子串是否順序包含 s2
for (int a = st[i], b = 0; a <= ed[k]; a++) {
if (s1[a] == s2[b]) {
b++;
// 匹配成功
if (b == len2) {
string temp = s1.substr(st[i], ed[k] - st[i] + 1);
// 沒有答案或答案更優時更新
if (ans.empty() || ans.length() > temp.length()) {
ans = temp;
}
// 找到的答案 == s2,即最優解,直接 return
if (ans.length() == len2) {
return;
}
}
}
}
}
}
}
int main() {
scanf("%s %s", str1, str2);
vector<int> idx;
string s1{ str1 }, s2{ str2 }, ans;
solve(s1, s2, ans);
puts(ans.c_str());
return 0;
}
貪心
#include <cstdio>
#include <vector>
#include <string>
using std::string;
using std::vector;
constexpr int MAX_N = 10050;
char str1[MAX_N], str2[MAX_N];
/**
* 正向貪心,舉例:atpaaabpabttpcat pat -> paaabpabt -> 有兩個 p
* 優先從最靠近 t 的 p 開始匹配,得到答案子串:pabt
* 即每次選擇距離尾字符最近的首字符,第一次匹配得到的答案一定是當前子串中最優的
* @param v 首字符位置數組
* @param s1 主串
* @param s2 “客”串
* @param ans 答案子串
* @param end 尾字符位置
*/
void findBest(vector<int>& v, string& s1, string& s2, string& ans, int end) {
int len = s2.length();
// 從后往前依次尋找
while (!v.empty()) {
// 模擬棧的存取
int index = v.back();
v.pop_back();
// 如果長度合理,則進行查找匹配
if (index + len - 1 <= end) {
int j = 0;
for (int i = index; i <= end; i++) {
if (s1[i] == s2[j]) {
j++;
}
}
// 滿足要求,檢驗答案是否更優
if (j == len) {
string temp = s1.substr(index, end - index + 1);
if (ans.empty() || temp.length() < ans.length()) {
ans = temp;
// 第一個即為當前最優,因此直接 return
return;
}
}
}
}
}
/**
* 反向貪心,舉例:atpaaabpabttpcat pat -> pcat
* @param v 尾字符位置數組
* @param s1 主串
* @param s2 “客”串
* @param ans 答案子串
* @param st 首字符位置
*/
void findBestRev(vector<int>& v, string& s1, string& s2, string& ans, int st) {
int len = s2.length();
// 幾乎和上一個函數一致,只修改了首尾字符的變量使用位置而已
while (!v.empty()) {
int end = v.back();
v.pop_back();
if (st + len - 1 <= end) {
int j = 0;
for (int i = st; i <= end; i++) {
if (s1[i] == s2[j]) {
j++;
}
}
if (j == len) {
string temp = s1.substr(st, end - st + 1);
if (ans.empty() || temp.length() < ans.length()) {
ans = temp;
return;
}
}
}
}
}
int main() {
scanf("%s %s", str1, str2);
vector<int> idx;
string s1{ str1 }, s2{ str2 }, ans;
int len1 = s1.length();
int len2 = s2.length();
// 無聊的正向匹配
for (int i = 0, j = 0; i < len1; i++) {
if (str1[i] == str2[0]) {
idx.push_back(i);
}
if (str1[i] == str2[j]) {
j++;
if (j == len2) {
findBest(idx, s1, s2, ans, i);
idx.clear();
j = 0;
}
}
}
// 無聊的反向匹配
for (int i = len1 - 1, j = len2 - 1; i >= 0; i--) {
if (str1[i] == str2[len2 - 1]) {
idx.push_back(i);
}
if (str1[i] == str2[j]) {
j--;
if (j == -1) {
findBestRev(idx, s1, s2, ans, i);
idx.clear();
j = len2 - 1;
}
}
}
puts(ans.c_str());
return 0;
}
let me try
貪心思路僅供參考,這里放暴力的吧
#include <cstdio>
#include <vector>
#include <string>
using std::string;
using std::vector;
constexpr int MAX_N = 10050;
char str1[MAX_N], str2[MAX_N];
/**
* 暴力匹配函數
* 通過預處理得到 P 的首尾字符在 S 中的所有下標位置
* 通過首位置與尾位置的相對位置篩選出最合適的位置進行查找
* 找到或找不到都進行一定的剪枝
* @param s1 主串 S
* @param s2 “客”串 P
* @param ans 最短子串
*/
void solve(string& s1, string& s2, string& ans) {
// 獲得 s2 的首尾字符在 s1 中的所有位置
// 暴力匹配
// 找到第一個合適的尾位置,即首尾位置確定的子串長度一定是要 >= s2 的長度的
// 檢查是否為合法子串
// 已有預選答案且當前子串的長度比答案字符串長,剪枝
// 開始匹配,看子串是否順序包含 s2
// 匹配成功
// 沒有答案或答案更優時更新
// 找到的答案 == s2,即最優解,直接 return
}
int main() {
scanf("%s %s", str1, str2);
vector<int> idx;
string s1{ str1 }, s2{ str2 }, ans;
// 調用函數
puts(ans.c_str());
return 0;
}
End
就題目難度而言,T3 才值 25 分吧!T4 比 T1 簡單,如果知道異或和map,那么 T2 也很簡單。。。
難度排序:T4 < T2 < T1 < T5 << T3
再也不說乙級簡單了。。。😭😭😭
題解僅供參考,若大家有思路更清晰,代碼量更少的解法,歡迎留言哦~~
大家友善討論,共同進步呀!