• leetcode 1505


    最多 K 次交换相邻数位后得到的最小整数

    题目大意:
    给你一个字符串 num 和一个整数 k 。其中,num 表示一个很大的整数,字符串中的每个字符依次对应整数上的各个 数位 。

    你可以交换这个整数相邻数位的数字 最多 k 次。

    请你返回你能得到的最小整数,并以字符串形式返回。

    题解

    贪心,每次将最小的数移动到当前字符串的最前端,判断能否移动。每次移动后,字符串都会改变(长度较少1),因此最基本的思路就是我们可以从最小的数字递归的进行计算

    def minInteger(self, num: str, k: int) -> str:
            if k<=0 or not num:
                return num
            for i in range(10):
                index=num.find(str(i))
                if index < 0:
                    continue
                if index<=k:
                    if index==k:
                        return str(i)+num[0:index]+num[index+1:]
                    else:
                        return str(i)+self.minInteger(num[0:index]+num[index+1:],k-index)
    

    但是上述的时间复杂度为O(n^2),虽然可以过,但显然不够好
    分析上述算法的过程我们可以发现,每次字符串查找的复杂度都是O(n)。我们可以用树状数组或字典树存储在当前置换位置i的前面有多少已经置换过了,这样我们只需要log(n)次查询(相当于区间求和)

        int n;
        int f[30010];
        int a[30010];
        int lowbit(int x)
        {
            return x&(-x);
        }
        void add(int x,int v)
        {
            while(x<=n)
            {
                f[x]+=v;
                x+=lowbit(x);
            }
        }
        int query(int x)
        {
            int res=0;
            while(x>=1)
            {
                res+=f[x];
                x-=lowbit(x);
            }
            return res;
        }
        string minInteger(string num, int k) {
            n=num.length();
            for(int i=1;i<=n;i++)a[i]=num[i-1]-'0',f[i]=0;
            vector<int>v[10];
            for(int i=1;i<=n;i++)
            {
                v[a[i]].push_back(i);
                add(i,1);
            }
            string ans="";
            for(int i=0;i<10;i++)
                reverse(v[i].begin(),v[i].end());
            int cur;
            while(ans.size()<n)
            {
                for(int i=0;i<10;i++)
                {
                    if(v[i].size()>0)
                    {
                        int cur=query(v[i].back())-1;
                        if(cur<=k)
                        {
                            k-=cur;
                            ans+=i+'0';
                            add(v[i].back(),-1);
                            v[i].pop_back();
                            break;
                        }
                    }
                }
            }
            return ans;
    

    有一个实现细节需要注意,每次我们都是从小到大寻找数字的,如果较大的数在前面,可能在某一论中较小的数无法置换。但是当该较大的数访问后,之前较小的数又可以置换。因此我们每次置换后,都需要从0开始,如果加个判断条件,时间复杂度依然是O(n^2)。因此我们可以利用vector不断的剔除置换过的数字,这样将减少重复访问的次数。

  • 相关阅读:
    4. ConcurrentHashMap 锁分段机制
    3. 原子变量-CAS算法
    2. 原子变量
    1. volatale 关键字 -内存可见性
    6.8 全局查询日志
    js实现数字分页
    拆箱和装箱
    string与stringbuilder的区别
    C#之out与ref的共性与区别以及用法
    asp.net操作xml(增删查改)
  • 原文地址:https://www.cnblogs.com/flightless/p/13281477.html
Copyright © 2020-2023  润新知