把过去和现在的笔记记录也搬了过来,也算是给以后留个念想吧,想想一开始打acm就是图一乐,后来发现这游戏还挺上头的,也算是度过了一段电竞生涯(xs)
早些时候的笔记写的好中二,连我自己看着都羞耻。
不过,就喜欢这种羞耻的感觉。
收录的题目大部分是个人认为质量不错的题目,以DP为主,非DP的题目都用※进行了标识。
当然,有些题解思路本身也是源自其他人的,不过除非特殊标注,否则都是用的自己的代码。

题目大意:

CF1624E Masha-forgetful
题目大意:给你n个长度为m的字符串(n*m不超过1e6),然后再给你一个串,问你能否将整个串表示为长度大于1的子串相加,其中这些子串一定要在前面的串中出现过,求每个子串的来源位置,输入任意即可。

解:

看似是很恐怖的字符串题目,实际上我们可以发现,对于每一个子串,它都可以分解为长度为2或3的子串相加,因此我们记录n个字符串中的子串的时候只需要记录长度为2和3的子串,在这里我直接用map来存哈希,map的value值用结构体(数字也行,然后再映射),表示这个子串出现的位置。
接下来就是遍历目标串来判定哪些是2哪些是3,这个时候就要用过到dp啦。
状态还是很好想的,dp[i]表示以i结尾的串是否合理,状态转移为dp[i]=dp[i-k]&&fk,记得在转移的同时记录下来转移的来源,最后倒推回去求得每个子串的来源位置。
题目给了三秒,我最后是702ms过的,内存消耗也不多,仔细想想,虽然有1e6的数据范围,但是长度为2和3的字母组成的串其实一共也没多少个吧(26^2+26^3),所以空间完全不用担心。
这题的dp含量其实很小了,毕竟不需要求最小的子串数量(要求的话这题就完全是另一道题了),哈希的建立也非常简单明显,考了一个记录路径但无足轻重,个人感觉不配2000分()

代码

代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <algorithm>
#include <utility>
#include <vector>
#include <istream>
#include <map>
#include <cmath>
#include <stack>
#include <set>
#include <cstring>
#include <string>
#define ll long long
#define maxn 200005
#define mdl 998244353
#define clr(a,n) for(int i=0;i<n;i++)a[i]=0
#define cfast std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
struct ck {
int start, end, num;
};
map< string, ck> has;
map<string, int> exi;
stack<ck> ans;
int dp[1001];
int main()
{
cfast;
int t;
cin >> t;
while (t--) {
int n, m;
cin >> n >> m;
has.clear();
exi.clear();
for (int i = 0; i <= m; i++)dp[i] = -1;
for (int i = 1; i <= n; i++) {
string s;
cin >> s;
for (int j = 0; j < m-1; j++) {
string tmp = "";
tmp += s[j];
tmp+=s[j + 1];
ck c1 = { j + 1,j + 2,i };
has[tmp] = c1;
exi[tmp] = 1;
//cout << "has." << tmp << "=" << c1.start << " " << c1.end << " " << c1.num << endl;
if (j < m-2) {
tmp += s[j + 2];
c1.end = j + 3;
has[tmp] = c1;
exi[tmp] = 1;
// cout << "has." << tmp << "=" << c1.start << " " << c1.end << " " << c1.num << endl;
}
}
}
string a;
cin >> a;
if (m == 1) {
cout << "-1" << endl;
continue;
}
int f = 0;
int flag = 0;
dp[0]=-1;
for (int j = 2; j <= m; j++) {
string tmp = "";
tmp += a[j - 2];
tmp += a[j-1];
if (exi[tmp] == 1&&(dp[j-2]!=-1||j-2==0)) {
dp[j] = j - 2;
//cout << "dp." << j << " = " << dp[j] << endl;
}
if (j > 2) {
tmp = "";
tmp += a[j - 3];
tmp += a[j - 2];
tmp += a[j-1];
if (exi[tmp] == 1 && (dp[j - 3] != -1 || j - 3 == 0)) {
dp[j] = j - 3;
// cout << "dp." << j << " = " << dp[j] << endl;
}
}
}
int now = m;
int num = 0;
if (dp[m] == -1) {
cout << "-1" << endl;
continue;
}
while (now>0) {
num++;
int tar = dp[now];
//cout << "tar=" << tar << endl;
string tmp = "";
for (int i = tar + 1; i <= now; i++)tmp += a[i-1];
ans.push(has[tmp]);
now = tar;
}
cout << num << endl;
while (!ans.empty()) {
ck c = ans.top();
printf("%d %d %d\n", c.start, c.end, c.num);
ans.pop();
}


}

}
/*
2
1 3 1 2 3
2 6 -1 4 -2 3 -2 3
*/