E2. Unforgivable Curse (hard version)

Problem - E2 - Codeforces

Problem description: Given two strings and a k. If the subscripts i and j satisfy |i - j| == k or |i - j| == k + 1, then yes swap(s[i], s[j]), s is one of the two strings.

Idea: If <i,z> <z,j>it works, then <i,j>it must be feasible. It is found that for a connected block, the characters in this connected block can always be exchanged. If for a connected block, the number of character types corresponding to the two strings in the connected block is the same, then you can always go through several operations to make the two strings correspond to the subscripts in the connected block. The elements are equal.

Therefore, DFS looks for a connected block, and then determines whether the character types and their corresponding numbers in the connected block are equal. If they are equal, they may be the same, and if they are not equal, they are definitely not equal.

AC code:

const int N = 2e5 + 21;
char s1[N], s2[N];
int fa[N]; // 并查集
// vis数组是判断连通块是否遍历过,st是判断这个点是否在找连通块中被遍历过
bool vis[N], st[N];
int find(int u) {
    
    return fa[u] == u ? u : fa[u] = find(fa[u]); }
void inpfile();
void solve() {
    
    
    int n,k; cin>>n>>k;
    // 初始化
    for(int i = 1;  i <= n; ++i) fa[i] = i, vis[i] = 0, st[i] = 0;
    cin>>s1 + 1>> s2 + 1;
    bool fg = true;
    map<int,int> mii;
    // 找连通块
    auto dfs = [&] (auto &&dfs, int u) -> void {
    
    
        if(st[u]) return ;
        st[u] = 1;
        int len = 0;
        for(int j = 0; j < 4; ++j) {
    
    
            len = (k + j%2) * (j < 2 ? 1 : -1);
            int pos = u + len;
            // 上面操作是得到 位置 
            // pos - k  |  pos + k
            // pos + k + 1 |  pos - (k + 1)

            // 位置不合法
            if(pos < 1 || pos > n) continue;

            // 找父亲
            int pfa = find(pos), ifa = find(u);
            if(pfa != ifa) {
    
    
                // 注意一定是将 pos的指向u的。因为后面遍历时,是根据第一个的位置来判断的
                fa[pfa] = ifa;
                /**
                 * 1 -- > 2 --> 3 -- > 4
                 * 将2的父亲指向1的父亲,这样在后面判断是否遍历过就行。
                 * 
                 * 由于我用的vis数组判断,如果用的是用map或者什么的,将每个点都进行个判断也行
                */

               // 判断字符种类及其个数是否相同
               // 这里用 一个字符串进行++操作,一个字符串进行--操作,等价于次
                mii[s1[pos]] ++;
                mii[s2[pos]] --;
                dfs(dfs, pos);
            }
        }
    };
    for(int i = 1; i <= n; ++i) {
    
    
        // 由于我是用这个父亲值进行判断的
        if(vis[find(i)]) continue;
        vis[find(i)] = 1;
        // 每次要清空
        mii.clear();
        
        // 第一个i,没有对应上,要手动设置上
        mii[s1[i]]++;
        mii[s2[i]]--;
        dfs(dfs, i);
        
        // 判断字符种类及其对应个数是否相同
        for(auto t: mii) {
    
    
            if(t.vs != 0) {
    
    
                fg = false;
            }
        }
    }
    puts(fg ? "YES" : "NO");

}

Guess you like

Origin blog.csdn.net/qq_63432403/article/details/132712062