CodeForces 1363F. Rotating Substrings

题意:你被给予了两个字符串s和t,每个字符串的长度都是n并且是小写字母,你的目标是让s变成t。
你可以进行如下的操作多次,使得字符串s变成字符串t,选择字符串s的子串并使得它旋转,即让(s[l, l + 1...r])变成字符串(s[r, l, l + 1...r - 1]),其它字符保持原有的位置。求最少的操作次数让字符串s变成字符串t,并确定是否它可以。

分析:每一个操作可以让一个字符提到前面的任何位置,并不能提到到后面,最少的操作次数是n - 两个字符串的最长公共子序列,但是这个最长公共子序列是受到限制的,受到什么限制呢?就是两个字符串匹配的时候,比如s的字符i和t的字符j匹配的时候,那么s的字符i后面的每种类型的字符数量都要大于t的j字符后面的每种类型的字符数量,这样s后面不匹配的字符可以提到前面来,我们求最长公共上升子序列的时候,我们的dp方程(dp[i][j] = max(dp[i][j], dp[i - 1][j - 1] + 1))不仅要满足s[i] == t[j],并且满足每种字符的后缀和suf_s[i][alpha]都大于等于suf_t[i][alpha]。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <string>
#include <algorithm>

using namespace std;
const int inf = 0x3f3f3f3f;

int main()
{
	int t;
	scanf("%d", &t);

	while (t--)
	{
		int n;
		scanf("%d", &n);

		string s, t;
		cin >> s >> t;

		string tmps = s, tmpt = t;
		sort(tmps.begin(), tmps.end()), sort(tmpt.begin(), tmpt.end());

		if (tmps != tmpt)
		{
			puts("-1");
			continue;
		}

		//偏移一个位置
		s = "?" + s;
		t = "?" + t;

		//dp数组
		vector<vector<int>> dp(n + 1, vector<int>(n + 1));
		//统计后缀中每个字符的数量
		vector<vector<int>> suf_s(n + 2, vector<int>(26)), suf_t(n + 2, vector<int>(26));

		for (int i = n; i >= 1; --i)
		{
			for (int j = 0; j < 26; ++j)
			{
				suf_s[i][j] = suf_s[i + 1][j];
				suf_t[i][j] = suf_t[i + 1][j];
			}
			++suf_s[i][s[i] - 'a'];
			++suf_t[i][t[i] - 'a'];
		}

		//求满足限制的最长公共子序列
		//初始化
		dp[0][0] = 0;
		for(int i = 1; i <= n; ++i)
			for (int j = 1; j <= n; ++j)
			{
				dp[i][j] = max(dp[i][j], max(dp[i - 1][j], dp[i][j - 1]));
				if (s[i] == t[j])
				{
					//必须满足限制,才能转移
					bool flag = true;
					for (int k = 0; k < 26; ++k)
					{
						if (suf_s[i][k] < suf_t[j][k])
							flag = false;
					}
					if (flag)
						dp[i][j] = max(dp[i][j], dp[i - 1][j - 1] + 1);
				}
			}
		printf("%d
", n - dp[n][n]);
	}

	
	return 0;
}








原文地址:https://www.cnblogs.com/pixel-Teee/p/13037647.html