D. Cyclic Rotation


D. Cyclic Rotation

题意:

给你两串数

你可以对第一串数多次操作: 选择l r满足a[l] == a[r] 将序列a[l]...a[r] 变成 a[l + 1] ... a[r] a[l] 判断最终a能否变成b (b是a的一个排列)

思路:

可以逆向思考 就相当于在b中有连续的 就可以取出一个放到前面任意一个位置

对于b串 k个连续的那么k-1个都可以由前面换位过来 

可以从后往前遍历b数组一一比对a数组  每次b数组遇到当前数与前面数相同时 储存当前数(用cnt数组记录储存的个数) 比对时如果两者相同 两指针都向前移动一位 当两者不相同时 那就判断 a[i]是否有储存的值 有的话直接对应掉 然后a数组指针前移 否则就输出no直到b数组都比对完 都符合就输出yes

#include
#define ll long long
#define ull unsigned long long
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;

const ll inf = 0x3f3f3f3f;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const double eps = 1e-4;
const int N = 1e6 + 5;
const int M = 1e5 + 5;
const int mod = 1e9 + 7;
ll n, a[N], b[N], vis[N], cnt[N];
ll pos1[N], pos2[N];
struct node {
    ll num, pos;
}aa[N], bb[N];

bool comp(node a, node b) {
    return a.pos < b.pos;
}

void solve()
{
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        cnt[i] = 0;
    }
    for (int i = 1; i <= n; i++) {
        cin >> b[i];
    }
    for (int i = n, j = n; j >= 1; ) {
        while (b[j] == b[j - 1]) {
            cnt[b[j]]++;
            j--;
            continue;
        }
        if (a[i] == b[j]) {
            i--;
            j--;
        }
        else if (cnt[a[i]]) {
            cnt[a[i]]--;
            i--;
        }
        else {
            cout << "NO\n";
            return;
        }
        
    }
    cout << "YES\n";
}

int main()
{
    IOS;
    int t = 1;
    cin >> t;
    while (t--)
    {
        solve();
    }
}
 

除了倒序遍历 其实也可以正序

正序方法:

开一个cnt数组 cnt[x] 存储需要从后面移过来多少个x

从前往后比对 如果ai == bj 两指针都往右移一位 如果没对上 分两种情况:

1.如果b[j] == b[j - 1] 说明b[j]可以移位到前面 判断一下前面是否需要该数 需要就直接cnt[b[j]]-1 然后j指针右移

2. b[j] != b[j - 1] 说明当前位置a[i] 需要一个数来对应 自动拿取一个数与ai对应掉 cnt[ai]就加1 i指针右移

当a数组已经遍历完而b数组还没遍历完时 说明前面需要置换过来的数 多于 后面能提供的数 那就不符合条件了 直接输出no

没有输出no 就输出yes即可

两种方法类似 理解了一种 另一种也很好理解 

第一种是倒序的 要先进行b[j] == b[j - 1]判断 是现将可以提供的数储存下来 后续需要时再判断该数是否有储存(重复的统一删除 需要的时候再取)
而第二种是正序 要先判断a[i] == b[j] 因为b数组只能从后面调取数与前面的数置换 前面能对应上的肯定是对应了的 (先拿取 再减重复的)

相关