a regular expression generator for number ranges

前端 未结 9 2155
长情又很酷
长情又很酷 2021-02-04 05:56

I checked on the stackExchange description, and algorithm questions are one of the allowed topics. So here goes.

Given an input of a range, where begin and ending number

9条回答
  •  北恋
    北恋 (楼主)
    2021-02-04 06:04

    Here's my solution and an algorithm with complexity O(log n) (n is the end of the range). I believe it is the simplest one here:

    Basically, split your task into these steps:

    1. Gradually "weaken" the start of the range.
    2. Gradually "weaken" the end of the range.
    3. Merge those two.

    By "weaken", I mean finding the end of range that can be represented by simple regex for this specific number, for example:

    145 -> 149,150 -> 199,200 -> 999,1000 -> etc.
    

    Here's a backward one, for the end of the range:

    387 -> 380,379 -> 300,299 -> 0
    

    Merging would be the process of noticing the overlap of 299->0 and 200->999 and combining those into 200->299.

    In result, you would get this set of numbers (first list intact, second one inverted:

    145, 149, 150, 199, 200, 299, 300, 379, 380, 387
    

    Now, here is the funny part. Take the numbers in pairs, and convert them to ranges:

    145-149, 150-199, 200-299, 300-379, 380-387
    

    Or in regex:

    14[5-9], 1[5-9][0-9], 2[0-9][0-9], 3[0-7][0-9], 38[0-7]
    

    Here's how the code for the weakening would look like:

    public static int next(int num) {
        //Convert to String for easier operations
        final char[] chars = String.valueOf(num).toCharArray();
        //Go through all digits backwards
        for (int i=chars.length-1; i>=0;i--) {
            //Skip the 0 changing it to 9. For example, for 190->199
            if (chars[i]=='0') {
                chars[i] = '9';
            } else { //If any other digit is encountered, change that to 9, for example, 195->199, or with both rules: 150->199
                chars[i] = '9';
                break;
            }
        }
    
        return Integer.parseInt(String.valueOf(chars));
    }
    
    //Same thing, but reversed. 387 -> 380, 379 -> 300, etc
    public static int prev(int num) {
        final char[] chars = String.valueOf(num).toCharArray();
        for (int i=chars.length-1; i>=0;i--) {
            if (chars[i] == '9') {
                chars[i] = '0';
            } else {
                chars[i] = '0';
                break;
            }
        }
    
        return Integer.parseInt(String.valueOf(chars));
    }
    

    The rest is technical details and is easy to implement. Here's an implementation of this O(log n) algorithm: https://ideone.com/3SCvZf

    Oh, and by the way, it works with other ranges too, for example for range 1-321654 the result is:

    [1-9]
    [1-9][0-9]
    [1-9][0-9][0-9]
    [1-9][0-9][0-9][0-9]
    [1-9][0-9][0-9][0-9][0-9]
    [1-2][0-9][0-9][0-9][0-9][0-9]
    3[0-1][0-9][0-9][0-9][0-9]
    320[0-9][0-9][0-9]
    321[0-5][0-9][0-9]
    3216[0-4][0-9]
    32165[0-4]
    

    And for 129-131 it's:

    129
    13[0-1]
    

提交回复
热议问题