JAVA研发笔试

真惨

1.A->B 最少多少步

记不清完整题干,形如011 到 110 最少需要多少步数,交换两个元素 或者 一次改变一个位数上的(0 1转变),或者 翻转字符串一次

上来就觉得像编辑距离,但是这里 多了个翻转 想了想几个思路 不感觉能直接上去dp 可能另有玄机 就先看第二题

事后思路:

不涉及添加 上来就固定长度n

只涉及交换 翻转 01跳变 三类操作

固定A,对B操作

看两个相似度 这里我想的是用异或 统计相同索引位置不同值的频次K(汉明距离)

针对每一个操作,向着减小,剩余操作数最大减小的方向传递

K可能不是最大的减小,但是经过操作后,剩余需要经过的操作数 是减小了最大的

首先 需要操作的索引位置 为 两字符串不一致的位置

然后 每一次 操作的选择 其实会有优先级

针对翻转

翻转B再计算相似度 记录K1 并记录剩余所需操作数

剩余所需操作数要比不翻转小2或者更高 才选择翻转 翻转本身会增加一次操作

针对交换

如果存在 两个索引位置 i 和 j上元素不同且刚好相反,则进行一次swap(i,j)实现K-2,count+1

针对01转变

对一个索引位置进行reverse(i) 实现K-1,count+1 显然01转变优先级最低

针对交换和01转变来说,可以合二为一

统计 0->1个数 和 1->0的个数, 比如有5个 需要从1到0 3个从0到1,那么最少策略为三个交换,剩下的两个01转变 总共所需操作数为5=max{num(0->1),num(1->0)}

然后就变成 每一次去判断 翻转 和 不翻转 之后需要操作数变化,选改变最大那个

比如:

A:1001010

B:0110011

k=5

count(0->1) = 3

count(1->0) = 2

不翻转情况下,实现两次交换,一次修改即可,需要3次操作,剩余操作数为3

翻转后

A:1001010

~B:1100110

k=3

count(0->1) = 2

count(1->0) = 1

翻转情况下,实现一次交换,一次修改即可,需要2次操作,剩余操作数为2 但是本身翻转算一次操作 所以2+1=3 没有变化 可以不翻转

但是 仔细一想

整个过程中, 如果需要翻转,那么最多只有一次有效翻转

因为这里固定长度 不涉及翻转后的插入,而且是整体翻转,并不支持子段的翻转,所以在这里两次翻转不存在的;

所以 贪心的初衷变成了 简单比较翻转与不翻转的方案 这条道就走通了

/*
 * @ClassName : newcoder
 * @Description : ali828 test
 * @Author : Dave
 * @Date: 2020-08-28 20:04
 **/
import java.util.*;
public class newcoder{
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = Integer.parseInt(sc.nextLine());
        String A = sc.nextLine();
        String B = sc.nextLine();
        System.out.print(new Solution().solve(n, A, B));
    }
}
class Solution{
    public int solve(int n, String A, String B) {
        return Math.min(getEditNum(A,B) , 1+getEditNum(A,new StringBuilder(B).reverse().toString()));
    }
    public int getEditNum(String A, String B) {
        /*
        * @Param [A, B]
        * @description   //获取字符串A B不翻转情况下最小操作数
        * @author Dave
        * @date 2020/8/28 21:13
        * @return int
        * @throws
        **/
        int n01 = 0, n10 = 0;
        for(int i = 0; i < A.length(); i++) {
            if(A.charAt(i) != B.charAt(i)){
                if(A.charAt(i) =='0'){
                    n01++;
                }else{
                    n10++;
                }
            }//if end
        }//for end
        return Math.max(n01, n10);
    }//method end
}

事后想问 笔试时咋想的?不能看着像编辑距离就往上面靠,老想着dp 其实这里 还真不好dp 因为翻转操作的是整个串,如果支持子段翻转,也许dp就可以搞 感觉也不好搞呀dp的起点咋整嘛 万一遇到直接中间翻转就解决问题的那种 真不好处理

2.无重复全排列无前导数 要求能够整除m

上来就觉得眼熟 全排列的升级版本 感觉dfs怼上去应该就可以了

其实 完蛋了比较笨的没有直接计算临时变量,用个队列去表示排列形式,然后在res添加时去判断 结果搞砸了

/*
不包含前导0  要剪掉  

比如输入 520 的情况下 025就不允许存在  

这一点 可以利用 自然数显示的时候 默认就不包含前导0 代替 所以 对于每一次组合 直接计算出int型的tmp值 
*/
import java.util.*;
public class newcoder {
    public static void main(String[] args) {
        //不包含前导0  要剪掉
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int m = in.nextInt();//这是要去整除的m

        //拆分转成数组
        ArrayList<Integer> list=new ArrayList<>();
        while(n/10!=0){
            list.add(n%10);
            n=n/10;
        }
        list.add(n);
        int[] nn=new int[list.size()];
        for (int i = 0; i < nn.length; i++) {
            nn[i]=list.get(i);
        }
        //todo
        //先排序 再dfs剪枝 去掉前导0和 不能整除m的元素
        Arrays.sort(nn);
        int len=nn.length;
        boolean[] used=new boolean[len];
        List<Integer> res=new LinkedList<>();//符合条件的排列 list
        int tmp=0;//记录临时变量
        dfs(nn,m,0,used,tmp,res);

        System.out.println(res.size());
    }
    public static void dfs(int[] nums,int m,int depth,boolean[] used,int tmp,List<Integer> res) {
        int len=nums.length;
        if(depth==len){
            if(tmp%m==0){
                //todo
                System.out.println(tmp);
                res.add(tmp);
            }
            return;
        }
        for(int i=0;i<len;++i){
            if(used[i]){//使用过的去掉
                continue;
            }
            //剪掉
            if(i>0 && nums[i]==nums[i-1] && !used[i-1]){
                continue;
            }

            tmp=tmp*10+nums[i];
            used[i]=true;

            dfs(nums,m,depth+1,used,tmp,res);

            //回溯回去
            used[i]=false;
            tmp=(tmp-nums[i])/10;
        }//fori end
    }//method end
}

/*TEST
502 2

52
250
502
520
4
*/

车轮滚滚 时间