zoukankan      html  css  js  c++  java
  • CF1430 E. String Reversal(div 2)

    题目链接:http://codeforces.com/contest/1430/problem/E

    题意:有一串长度为n(n<=2*10^5)由小写字母组成的字符串,求通过相邻交换得到其反转串(回文串)得最少交换次数

    思路:通过这道题学会了一些奇怪而又实用的小知识(雾,一般相邻交换会想到逆序对数---->一个数列的逆序对数是其通过相邻交换恢复为自然序列的最小交换次数

       因此我们可以将字符串逆序给予编号,这样的话就可以用逆序对数解决问题啦

       但是!!!怎么解决相同的字符?我们已知相同的字符间是等价的,因此如果我们要使交换次数最小,不妨使相同字符的编号从小排到大(使其内部间的逆序对数=0)

       实现方式是枚举a-z,分别得到相同字符的子串,然后反转编号,复杂度为O(26*n+n)   (或者在输入时可以用链表优化,复杂度O(2*n))

       操作完后用树状数组求逆序对数即可

    代码:

    #include <cstdio>
    #define lowbit(x) x&(-x)
    #define LL long long
    char s[200100];
    int n,a[200100],cnt,head[200100],num;
    LL ans;
    struct BIT{
        int tr[200100];
        void add(int x)
        {
            for(;x<=n;x+=lowbit(x)) tr[x]++;
            return;
        }
        LL query(int x)
        {
            LL sum=0;
            for(;x;x-=lowbit(x)) sum+=tr[x];
            return sum;
        }
    }P;
    void exch(char x)
    {
        int t;
        num=0;
        for(int i=1;i<=n;i++)
        if (s[i]==x) head[++num]=i;
        for(int i=1;i<=num/2;i++)
        {t=a[head[i]]; a[head[i]]=a[head[num-i+1]]; a[head[num-i+1]]=t;}
        return;
    }
    int main()
    {
        scanf("%d",&n); cnt=1;
        scanf(" %s",s+1);
        a[n]=cnt;
        for(int i=n-1;i>=1;i--)
        {
            if (s[i]!=s[i+1]) cnt++;
            a[i]=cnt;
        }
        for(int i=0;i<26;i++) exch('a'+i);
        for(int i=1;i<=n;i++)
        {
            ans+=P.query(cnt)-P.query(a[i]);
            P.add(a[i]);
        }
        printf("%lld",ans);
        return 0;
    }
  • 相关阅读:
    phpcms调用指定文章内容模型的ID
    phpcms V9的各种功能总结
    如何使用ajax与php实现简单的流程管理
    键盘按键和键盘对应代码表
    ajax实现省、市、区、三级联动(例题)
    用ajax做登录与删除
    var_dump、echo、print_r 的区别
    字符串删除重复字符
    树的非递归遍历
    字符串操作_(不使用库函数)
  • 原文地址:https://www.cnblogs.com/ctrl-newbee/p/13864068.html
Copyright © 2011-2022 走看看