1

I'm currently doing an online course on hyperskill. There's a task:

The password is hard to crack if it contains at least A uppercase letters, at least B lowercase letters, at least C digits and includes exactly N symbols. Also, a password cannot contain two or more same characters coming one after another. For a given numbers A, B, C, N you should output password that matches these requirements.

And here's my code:

import java.util.*;

public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int upper = scan.nextInt();
        int lower = scan.nextInt();
        int digits = scan.nextInt();
        int quantity = scan.nextInt();
        String symbolsUpper = "QWERTYUIOPASDFGHJKLZXCVBNM";
        String symbolsLower = "qwertyuiopasdfghjklzxcvbnm";
        String symbolsDigits = "1234567890";
        boolean exit = false;
        Random random = new Random();
        ArrayList<Character> password = new ArrayList<>();
        if (upper > 0) {
            for (int i = 0; i < upper; i++) {
                password.add(symbolsUpper.charAt(random.nextInt(symbolsUpper.length())));
            }
        }
        if (lower > 0) {
            for (int k = 0; k < lower; k++) {
                password.add(symbolsLower.charAt(random.nextInt(symbolsLower.length())));
            }
        }
        if (digits > 0) {
            for (int z = 0; z < digits; z++) {
                password.add(symbolsDigits.charAt(random.nextInt(symbolsDigits.length())));
            }
        }
        if (quantity - digits - upper - lower > 0) {
            for (int m = 0; m < (quantity - digits - upper - lower); m++) {
                password.add(symbolsDigits.charAt(random.nextInt(symbolsDigits.length())));
            }
        }
        Collections.shuffle(password);
        while (!exit) {
            if (password.size() > 1) {
                for (int i = 1; i < password.size(); i++) {
                    if (password.get(i).equals(password.get(i - 1))) {
                        char buffer = password.get(i);
                        password.remove(i);
                        password.add(buffer);
                        i--;
                    } else {
                        exit = true;
                    }
                }
            } else {
                exit = true;
            }
        }
        StringBuilder buildPassword = new StringBuilder();
        for (Character character : password) {
            buildPassword.append(character);
        }
        System.out.println(buildPassword);
    }
}

When I run the code in IntelliJ IDEA, the program works just fine, however, the hyperskill platform doesn't accept this code as the right one. The topic is "Processing string".

Can anyone here tell me please, what am I doing wrong? Is there a better way to write this code?

5
  • 2
    Your code will fail if input is 2 0 0 2 and the random generator happens to pick the same letter twice. Commented Dec 30, 2019 at 12:22
  • 1
    well, that's the problem here, as actually it doesn't fail.. Can you please suggest smth to improve the code? Commented Dec 30, 2019 at 12:26
  • How should I edit the code? As I keep testing and it works Commented Dec 30, 2019 at 12:33
  • 1
    That looks like a challenge or assignment for you to finish. Your question was 'what am I doing wrong?" and I've pointed out one possible problem with the code. You should attempt to fix the problem yourself, before asking here, which is a new question, so don't ask that in this question. Commented Dec 30, 2019 at 12:37
  • 2
    @Andetleenew Run your code with huge passwords like 0 0 0 20000 and it will not generate a password (depending on the seed of the Random object) because it cannot generate a password, because there are two same characters next to each other. Commented Dec 30, 2019 at 12:38

2 Answers 2

1

Can anyone here tell me please, what am I doing wrong?

The problem is that, due to the nature of random numbers, you might be very unlucky in the characters which are picked. This can result in two problems:

  1. You can pick the same characters from the pool of characters. When you create a password using the input 0 0 0 2 it might be possible that two same digits are picked. As an example the password "55" can never satisfy the condition of having not two characters next to each other be the same, no matter how many time you shuffle it.

  2. When the password is very long and you find two characters same next to each other you put one of the character to the end. This can happen twice for the same character. This means that the password "........44........44........." can result in the password ".........4.........4...........44", and now you have two same characters again (at the end).

Is there a better way to write this code?

Yes.

Sign up to request clarification or add additional context in comments.

2 Comments

Could you suggest what I should improve in the code?
Yes, but I'm not the one doing the online course on hyperskill. The naive approach would be "password correct? if not, try again".
0

I don't know if you are trying for best performance, but here is a fun solution:

public static void main(String[] args) {
    Scanner scan = new Scanner(System.in);
    int uppers = scan.nextInt();
    int lowers = scan.nextInt();
    int digits = scan.nextInt();
    int quantity = scan.nextInt();
    int freeChoices = quantity - uppers - lowers - digits;
    if (freeChoices < 0) {
        System.exit(1);
    }
    ThreadLocalRandom r = ThreadLocalRandom.current();
    StringBuilder password = new StringBuilder();
    boolean isPasswordReady = false;
    int lastUpper = -1, lastLower = -1, lastDigit = -1;
    PasswordPart[] options = PasswordPart.values();
    while (!isPasswordReady) {
        int partChoice = r.nextInt(0, options.length);
        switch (options[partChoice]) {
            case DIGIT:
                if (digits > 0 || freeChoices > 0) {
                    CharIndexHolder result = options[partChoice].get(lastDigit, -1, r);
                    password.append(result.c);
                    lastDigit = result.i;
                    if (digits == 0) {
                        freeChoices--;
                    } else {
                        digits--;
                    }
                }
                break;
            case LOWER:
                if (lowers > 0 || freeChoices > 0) {
                    CharIndexHolder result = options[partChoice].get(lastLower, lastUpper, r);
                    password.append(result.c);
                    lastLower = result.i;
                    if (lowers == 0) {
                        freeChoices--;
                    } else {
                        lowers--;
                    }
                }
                break;
            case UPPER:
                if (uppers > 0 || freeChoices > 0) {
                    CharIndexHolder result = options[partChoice].get(lastUpper, lastLower, r);
                    password.append(result.c);
                    lastUpper = result.i;
                    if (uppers == 0) {
                        freeChoices--;
                    } else {
                        uppers--;
                    }
                }
                break;

        }

        isPasswordReady = uppers == 0 && lowers == 0 && digits == 0 && freeChoices == 0;
    }

    System.out.println(password.toString());
}

enum PasswordPart {
    UPPER("QWERTYUIOPASDFGHJKLZXCVBNM"), LOWER("qwertyuiopasdfghjklzxcvbnm"), DIGIT("1234567890");
    private String pool;

    PasswordPart(String pool) {
        this.pool = pool;
    }

    public CharIndexHolder get(int lastIndex, int additionalIndex, ThreadLocalRandom random) {
        int i = random.nextInt(0, pool.length());
        while (i == lastIndex || i == additionalIndex) {
            i = random.nextInt(0, pool.length());
        }

        return new CharIndexHolder(pool.charAt(i), i);
    }
}

private static class CharIndexHolder {
    char c;
    int i;


    CharIndexHolder(char c, int i) {
        this.c = c;
        this.i = i;
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.