LeetCode 316. Remove Duplicate Letters(删除重复字母)

49 篇文章 0 订阅
34 篇文章 0 订阅

原题网址:https://leetcode.com/problems/remove-duplicate-letters/

Given a string which contains only lowercase letters, remove duplicate letters so that every letter appear once and only once. You must make sure your result is the smallest in lexicographical order among all possible results.

Example:

Given "bcabc"
Return "abc"

Given "cbacdcbc"
Return "acdb"

方法一:逐个字母找出。例如要找出第一个字母,首先找到最右边的一个点,使该点右边能够包含全部的字母,则第一个字母必在该点左边(包含该点),寻找方法是从该点向左搜寻,搜寻到一个字母表最靠前的、位置最靠左的字母,则为第一个字母。

public class Solution {
    public String removeDuplicateLetters(String s) {
        char[] sa = s.toCharArray();
        int[] alphabet = new int[26];
        int count = 0;
        for(int i=0; i<sa.length; i++) {
            if (alphabet[sa[i]-'a']==0) count ++;
            alphabet[sa[i]-'a'] ++;
        }
        char[] removed = new char[count];
        int left = 0;
        int right = sa.length;
        int c = 0;
        Arrays.fill(alphabet, 0);
        while (c<count) {
            right --;
            if (alphabet[sa[right]-'a']==0) c ++;
            alphabet[sa[right]-'a'] ++;
        }
        for(int i=0; i<count; i++) {
            char ch = sa[right];
            int leftmost=right;
            for(int j=right-1; j>=left; j--) {
                if (sa[j] <= ch && alphabet[sa[j]-'a'] != 0) {
                    ch = sa[j];
                    leftmost = j;
                }
            }
            alphabet[ch-'a'] = 0;
            removed[i] = ch;
            left = leftmost + 1;
            while (right<sa.length-1 && alphabet[sa[right]-'a'] != 1) {
                if (alphabet[sa[right]-'a'] > 1) alphabet[sa[right]-'a'] --;
                right ++;
            }
        }
        return new String(removed);
    }
}

另一种实现:

public class Solution {
    public String removeDuplicateLetters(String s) {
        int[] f = new int[26];
        char[] sa = s.toCharArray();
        int count = 0;
        for(int i=sa.length-1; i>=0; i--) {
            if (f[sa[i]-'a'] == 0) count ++;
            f[sa[i]-'a'] ++;
        }
        char[] unique = new char[count];
        Arrays.fill(unique, (char)0xff);
        int from = 0, to = 0;
        int pos = 0;
        for(int i=0; i<count; i++) {
            while (to<sa.length && (f[sa[to]-'a']!=1)) f[sa[to++]-'a']--;
            for(int j=from; j<=to; j++) {
                if (f[sa[j]-'a']>0 && sa[j] < unique[i]) {
                    unique[i] = sa[j];
                    pos = j;
                }
            }
            while (pos+1<to) f[sa[--to]-'a']++;
            f[unique[i]-'a'] = 0;
            from = pos + 1;
            to = from;
        }
        return new String(unique);
    }
}

又一个实现:

public class Solution {
    public String removeDuplicateLetters(String s) {
        if (s == null || s.length() == 0) return s;
        char[] sa = s.toCharArray();
        int[] f = new int[26];
        int len = 0;
        for(int i=0; i<sa.length; i++) {
            if (f[sa[i]-'a'] ++ == 0) len ++;
        }
        char[] unique = new char[len];
        int from = 0;
        for(int i=0; i<unique.length; i++) {
            int right = from;
            int pos = -1;
            while (right < sa.length && f[sa[right++]-'a']-- != 1);
            for(int j=from; j<right; j++) {
                if (f[sa[j]-'a'] >= 0 && (pos==-1 || sa[j]<sa[pos])) pos = j;
            }
            unique[i] = sa[pos];
            for(int j=pos; j<right; j++) {
                f[sa[j]-'a'] ++;
            }
            f[sa[pos]-'a'] = 0;
            from = pos + 1;
        }
        return new String(unique);
    }
}

方法二:使用堆。

public class Solution {
    public String removeDuplicateLetters(String s) {
        char[] sa = s.toCharArray();
        int[] f = new int[26];
        int count = 0;
        // 统计不同的字符个数
        for(char ch : sa) {
            if (f[ch - 'a']++ == 0) count++;
        }
        char[] result = new char[count];
        PriorityQueue<Character> heap = new PriorityQueue<>();
        int i = 0;
        int from = 0, to = 0;
        while (i < result.length) {
            // 找到最大可能靠后的候选字符位置
            while (to < sa.length) {
                if (f[sa[to] - 'a'] > 0) {
                    heap.offer(sa[to]);
                    if (--f[sa[to++] - 'a'] == 0) break;
                } else {
                    to++;
                }
            }
            char ch = (char)0;
            do {
                ch = heap.poll();
                while (heap.remove(ch));
                result[i++] = ch;
                f[ch - 'a'] = 0;
                // 该字符前面的字符已经作废,从堆中删除
                while (sa[from++] != ch) {
                    heap.remove(sa[from - 1]);
                }
            // 如果最大可能靠后的候选字符已经被选择,则终止
            } while (ch != sa[to - 1]);
        }
        return new String(result);
    }
}

方法三:使用二叉搜索树。

public class Solution {
    int size = 0;
    int[] set = new int[26];
    int[] tree = new int[26];
    private void offer(int val) {
        int i = 0, j = 25;
        while (i <= j) {
            int m = (i + j) >> 1;
            if (m < val) {
                i = m + 1;
            } else {
                tree[m] ++;
                j = m - 1;
            }
        }
        size ++;
        set[val]++;
    }
    private boolean remove(int val) {
        if (size == 0 || set[val] == 0) return false;
        int i = 0, j = 25;
        while (i <= j) {
            int m = (i + j) >> 1;
            if (m < val) {
                i = m + 1;
            } else {
                tree[m] --;
                j = m - 1;
            }
        }
        size--;
        set[val]--;
        return true;
    }
    private int min() {
        int i = 0, j = 25;
        while (i <= j) {
            int m = (i + j) >> 1;
            if (tree[m] > 0) {
                tree[m] --;
                j = m - 1;
            } else {
                i = m + 1;
            }
        }
        size--;
        set[i]--;
        return i;
    }
    public String removeDuplicateLetters(String s) {
        char[] sa = s.toCharArray();
        int[] f = new int[26];
        int count = 0;
        // 统计不同的字符个数
        for(char ch : sa) {
            if (f[ch - 'a']++ == 0) count++;
        }
        char[] result = new char[count];
        int i = 0;
        int from = 0, to = 0;
        while (i < result.length) {
            // 找到最大可能靠后的候选字符位置
            while (to < sa.length) {
                if (f[sa[to] - 'a'] > 0) {
                    offer(sa[to] - 'a');
                    if (--f[sa[to++] - 'a'] == 0) break; 
                } else {
                    to++;
                }
            }
            char ch = (char)0;
            do {
                ch = (char)('a' + min());
                while (remove(ch - 'a'));
                result[i++] = ch;
                f[ch - 'a'] = 0;
                // 该字符前面的字符已经作废,从搜索树中删除
                while (sa[from++] != ch) {
                    remove(sa[from - 1] - 'a');
                }
            // 如果最大可能靠后的候选字符已经被选择,则终止
            } while (from < to && ch != sa[to - 1]);
        }
        return new String(result);
    }
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值