说实话
题目其实不难
需要你静下心来想
—— 一只蒟蒻的想法╮(╯▽╰)╭
【题目描述】
给出两个长度相同且由大写英文字母组成的字符串A、B,保证A和B中每种字母出现的次数相同。
现在每次可以交换A中相邻两个字符,求最少需要交换多少次可以使得A变成B。
【输入格式】
第一行一个正整数
n
(
2
<
=
n
<
=
1
,
000
,
000
)
n (2<=n<=1,000,000)
n(2<=n<=1,000,000),表示字符串的长度。
第二行和第三行各一个长度为
n
n
n 的字符串,并且只包含大写英文字母。
【输出格式】
一行,一个整数,表示答案。
【样例输入输出】
样例输入:
3
ABC
BCA
样例输出:
2
样例解释:ABC -> BAC -> BCA
【数据规模与约定】
时间限制:
1
s
1s
1s。
空间限制:
256
M
B
256MB
256MB。
【解析】
显然,求最小交换次数就是求逆序对的个数 至于证明,感兴趣的同学可以上网查找 。
可能不太懂,没关系,我们举个例子。
假设:
A
:
A
B
C
B
C
C
A
B
:
B
C
C
B
C
A
A
A:\qquad A\qquad B\qquad C\qquad B\qquad C\qquad C\qquad A\\ B:\qquad B\qquad C\qquad C\qquad B\qquad C\qquad A\qquad A
A:ABCBCCAB:BCCBCAA先将
A
A
A 中的字母按升序标号:
然后,我们将其与B一一配对:因为交换次数要最小,所以肯定倒着找从A中对应的B中左边最近的配对,若没有,则正着找右边最近的(嗯 似乎不太好理解,没关系,下面会详解)
那么,我们需要知道如何求出B中各个字母原来在A序列中的位置。
因此,开一个数组
p
o
s
[
I
]
pos[I]
pos[I] 记录与字母
I
I
I 相等的最近的字母
J
J
J 且
J
<
I
J<I
J<I 的位置,先扫描A序列,将其存在一个升序的数组
f
[
i
]
f[i]
f[i] 中 ,接着倒叙扫描B序列,通过
f
[
i
]
f[i]
f[i] 传递找到在A序列中的位置,储存到一个
s
[
i
]
s[i]
s[i] 数组中,然后,根据之前的思路,用树状数组对
s
[
i
]
s[i]
s[i] 求逆序对,即为所求。
【核心代码】
for(int i=0;i<n;++i)
{
f[i+1]=pos[s1[i]-'A'+1];
pos[s1[i]-'A'+1]=i+1;
}
for(int i=n-1;i>=0;--i)
{
s[i+1]=pos[s2[i]-'A'+1];
pos[s2[i]-'A'+1]=f[pos[s2[i]-'A'+1]];
}
如果还不能很理解,我们就这上面的样例模拟一遍:
1、第一个循环
i | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
---|---|---|---|---|---|---|---|
f[i] | f[1]=pos[A]=0 | f[2]=pos[B]=0 | f[3]=pos[C]=0 | f[4]=pos[B]=2 | f[5]=pos[C]=3 | f[6]=pos[C]=5 | f[7]=pos[A]=1 |
pos[I] | pos[A]=1 | pos[B]=2 | pos[C]=3 | pos[B]=4 | pos[C]=5 | pos[C]=6 | pos[A]=7 |
注解:第一次循环后,数组的值:
f
[
1
]
=
0
,
f
[
2
]
=
0
,
f
[
3
]
=
0
,
f
[
4
]
=
2
,
f
[
5
]
=
3
,
f
[
6
]
=
5
,
f
[
7
]
=
1
p
o
s
[
A
]
=
7
,
p
o
s
[
B
]
=
4
,
p
o
s
[
C
]
=
6
\begin{aligned}& f[1]=0,f[2]=0,f[3]=0,f[4]=2,f[5]=3,f[6]=5,f[7]=1\\ &pos[A]=7,pos[B]=4,pos[C]=6\end{aligned}
f[1]=0,f[2]=0,f[3]=0,f[4]=2,f[5]=3,f[6]=5,f[7]=1pos[A]=7,pos[B]=4,pos[C]=6
2、第二个循环
i | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|
s[i] | s[7]=pos[A]=7 | s[6]=pos[A]=1 | s[5]=pos[C]=6 | s[4]=pos[B]=4 | s[3]=pos[C]=5 | s[2]=pos[C]=3 | s[1]=pos[B]=2 |
pos[I] | pos[A]=f[pos[A]]=f[7]=1 | pos[A]=f[pos[A]]=f[1]=0 | pos[C]=f[pos[C]]=f[6]=5 | pos[B]=f[pos[B]]=f[4]=2 | pos[C]=f[pos[C]]=f[5]=3 | pos[C]=f[pos[C]]=f[3]=0 | pos[B]=f[pos[B]]=f[2]=0 |
注解:第二次循环后,数组的值:
s
[
1
]
=
2
,
s
[
2
]
=
3
,
s
[
3
]
=
5
,
s
[
4
]
=
4
,
s
[
5
]
=
6
,
s
[
6
]
=
1
,
s
[
7
]
=
7
p
o
s
[
A
]
=
0
,
p
o
s
[
B
]
=
0
,
p
o
s
[
C
]
=
0
\begin{aligned} &s[1]=2,s[2]=3,s[3]=5,s[4]=4,s[5]=6,s[6]=1,s[7]=7\\ &pos[A]=0,pos[B]=0,pos[C]=0\end{aligned}
s[1]=2,s[2]=3,s[3]=5,s[4]=4,s[5]=6,s[6]=1,s[7]=7pos[A]=0,pos[B]=0,pos[C]=0A序列中每个字母在B序列中的位置如下:
对应关系即为
最后我们只要建立
s
[
i
]
s[i]
s[i] 的树状数组求逆序对个数即可。
【代码展示】
#include<bits/stdc++.h>
#define ud using namespace std
#define ll long long
ud;
const int maxn=1e6+1000;
int n;
string s1,s2;
int c[maxn],f[maxn],pos[maxn],s[maxn];
inline long long read()
{
long long sum=0,flag=1;
char c;
for(;c<'0'||c>'9';c=getchar())if(c=='-') flag=-1;
for(;c>='0'&&c<='9';c=getchar())sum=(sum<<1)+(sum<<3)+c-'0';
return sum*flag;
}
void add(int x,int y)
{
for(;x<=n;x+=x&(-x))
c[x]+=y;
}
int ask(int x)
{
int ans=0;
for(;x;x-=x&(-x))
ans+=c[x];
return ans;
}
int main()
{
n=read();
cin>>s1;
cin>>s2;
for(int i=0;i<n;++i)
{
f[i+1]=pos[s1[i]-'A'+1];
pos[s1[i]-'A'+1]=i+1;
}
for(int i=n-1;i>=0;--i)
{
s[i+1]=pos[s2[i]-'A'+1];
pos[s2[i]-'A'+1]=f[pos[s2[i]-'A'+1]];
}
ll ans=0;
for(int i=1;i<=n;++i)//也可以倒序求逆序对
{
ans+=ask(n)-ask(s[i]);
add(s[i],1);
}
printf("%lld\n",ans);
return 0;
}
好了,这次的讲解就到这里,大家如果有点累了,可以放松一下。