2021 ICPC Asia Taipei Regional
代码1:TLE
#include<string>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
typedef pair<int, int>PII;
const int N = 1010;
string str_r(string str, int i, int j)
{
reverse(str.begin() + i - 1, str.begin() + j);
return str;
}
int get_m(string s1, string s2)
{
int res = 0;
for (int i = 0; i < s1.size(); i++)
if (s1[i] == s2[i])res++;
return res;
}
bool cmp(PII a, PII b)
{
if (a.second - a.first != b.second - b.first)return a.second - a.first < b.second - b.first;
else return a.first < b.first;
}
int main()
{
int t; cin >> t;
while (t--)
{
vector<PII>ans;
int lenth;
cin >> lenth;
string s1, s2;
cin >> s1 >> s2;
int m12 = 0;
for (int i = 0; i < lenth; i++)
if (s1[i] == s2[i])m12++;
int res = m12;
ans.push_back({ 1,1 });
for (int i = 1; i <= lenth - 1; i++)
for (int j = i + 1; j <= lenth; j++)
{
string re = str_r(s2, i, j);
int t = get_m(s1, re);
if (t == res)
ans.push_back({ i,j });
if (t > res)
{
res = t;
ans.clear();
ans.push_back({ i,j });
}
}
sort(ans.begin(), ans.end(), cmp);
cout << m12 << ' ' << res << ' ' << ans.front().first << ' ' << ans.front().second << endl;
}
return 0;
}
代码2:AC
思路:
要求翻转某一段字串后的s2与s1匹配数最大
原问题转化为分析哪一段子串最值得翻转
两重循环的暴力求解
枚举{i,j}(i<j)分析每次翻转获得匹配数并求最大值
同时要减去副作用——翻转后会损失原先已经匹配好的
于是我们要求的就是差值最大(翻转后新匹配-原匹配)这个i和j
相同的差值就求|i-j|最小的那一个
#include <bits/stdc++.h>
using namespace std;
int match[1002][1002];
//获得正反匹配数目的差值
int _mat(string s1, string s2)
{
int sum1 = 0, sum2 = 0;
int len = s1.size();
for (int i = 0; i < len; i++)
{
if (s1[i] == s2[i])
{
sum2++;
}
if (s1[i] == s2[len - 1 - i])
{
sum1++;
}
}
return sum1 - sum2;
}
int main()
{
int t;
cin >> t;
while (t--)
{
int n;
int ansi, ansj, mxi = 0, is = 0;
cin >> n;
string s1, s2;
cin >> s1 >> s2;
memset(match, 0, sizeof(match));
for (int i = 0; i < n - 1; i++)
{
for (int j = i + 1; j < n; j++)
{
//取从第i位到第j位的子串
//.substr(i,j-i+1)
match[i][j] = _mat(s1.substr(i, j - i + 1), s2.substr(i, j - i + 1));
}
}
for (int i = 0; i < n - 1; i++)
{
is += (s1[i] == s2[i]);//原串未翻转时的匹配数
for (int j = i + 1; j < n; j++)
{
if (mxi < match[i][j])
{
ansi = i, ansj = j;
mxi = match[i][j];
}
else if (mxi == match[i][j])
{
if (j - i < ansj - ansi)
{
ansi = i, ansj = j;
}
}
}
}
is += (s1[n - 1] == s2[n - 1]);
if (!mxi)
cout << is << " " << is + mxi << " " << 1 << " " << 1 << '\n';
else
cout << is << " " << is + mxi << " " << ansi + 1 << " " << ansj + 1 << '\n';
}
return 0;
}
分析:
代码1与代码2都是O(n^3)的时间复杂度 但常数不同
代码1粗暴模拟了所有的翻转情况并进行比较
对于每一个string通过函数调用进行了实际的翻转操作获得新的string
并且代码1对每一个获得新串从头到尾进行了匹配计算
代码2只是在思路上暴力模拟 但并没有实际对string进行翻转操作
并且代码2考虑的是 最值得翻转的情况
因此在统计匹配数目的时候 只是对需要进行翻转的字串进行计算
而不像代码1那样 对翻转后的整个字串进行匹配计算
总结:
1.翻转后倒着匹配可以这么做 不需要对string真的reverse
if (s1[i] == s2[len - 1 - i])sum1++;
2. 考虑问题的影响范围
对于局部的字串进行翻转
其他部分的字符串没有影响 可以不参与分析比较
因此在本题中 枚举所有区间 进行翻转前后的差值比较 是公平合理的