ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 문자열
    자료구조와 알고리즘/문제풀기 2024. 1. 10. 18:41

    - 팰린드롬을 검사하는 간단한 문제이다.

    - 단 문자열과 숫자만 비교해야한다.

     

     > Leetcode (125) - 유효한 팰린드롬

     

    나의 풀이 (13ms)

      public boolean isPalindrome(String s) {
            s = s.toLowerCase();
            s = s.replaceAll("[^a-z0-9]","");
    
    
                int left = 0;
                int right = s.length()-1;
    
                for(int i=0; i<s.length();i++ ){
                    if(left > right){
                        return true;
                    }
                    if(s.charAt(left) == s.charAt(right)){
                        left++;
                        right--;
                    }else{
                        return false;
                    }
                }
                return true;
        }

     

     

     

     

    1. 문자 단위로 추출해서 처리 (2ms)

     

      public boolean isPalindrome(String s) {
           int start =0;
           int end = s.length() - 1;
    
    	while(start < end){
    		if(!Character.isLetterOrDigit(s.charAt(start))){
        		start++;
       	   }else if(!Character.isLetterOrDigit(s.charAt(end))){
        	    end--;
           }else{
        
           if(Character.toLowerCase(s.charAt(start))!=Character.toLowerCase(s.charAt(end))){
        	  return false;
           }
             start++;
             end--;
          }
      }
    
          return true;
        }

     

     

    2. 문자바로비교 (16ms)

     

    public boolean isPAlindrome(String s){
    
    	String s_filtered = s.replaceAll("[^A-Za-z0-9]","").toLowerCase();
        String s_reversed = new StringBuilder(s_filtered).reverse().toString();
          
          
          return s_filtered.equals(s_reversed);
    }

     

     

    - 배운 것 

     

    Chracter.isLetterOrDigit으로 숫자와 문자만 검사할 수 있다.

    [^A-Za-z0-9] 정규식을 통한 제거 

    left,right로 하는 것은 좋았으나, 검사를 다르게 하는게 더 빨랐을 것 같다. 

     

     

    > 문자열 뒤집기 LeetCode(344) 

     

    1. 나의 풀이 (0ms)

     

     public void reverseString(char[] s) {
            int len = s.length;
    
            for(int i=0; i<len/2;i++){
                char temp = s[i];
                s[i] = s[len-1-i];
                s[len-1-i] = temp;
            }
        }

     

     

    2.  풀이 스타일이 비슷하다! 

    int start = 0;
    int end  = s.length - 1;
    
    while(start < end){
    
    		char temp = s[start];
            s[start] = s[end];
            s[end] = temp;
            
            start++;
            end--;
    
    }

     

     

    - 배열 반만 도는 것 while문으로 표현하면 start<end로 start++, end-- 생각할 수 있다. (시작과 끝에서 한칸씩 오는경우)

     

     

     

    > 가장 흔한 단어 (819) 

     

    1. 나의 풀이 (26)

    public String mostCommonWord(String paragraph, String[] banned) {
          
          //a-z말고 다른 단어 모두 제거 
          paragraph = paragraph.toLowerCase().replaceAll("[^a-z]"," ");
    		
            //띄어쓰기 두개이상인것 하나로 만들고 trim
            paragraph = paragraph.replaceAll("\\s+"," ").trim();
            
            //split로 쪼개기
            String[] arr = paragraph.split(" ");
          
            List<Integer> idxarr = new ArrayList<>();
    		
            for(int i=0; i<arr.length;i++){
                int cnt =0;
                if(bannedString(arr[i],banned)){
                    idxarr.add(0);
                    continue;
                }else{
                    for(int j=i+1; j<arr.length; j++){
                    	 //공백이 아니고 같은 단어일 경우 cnt++
                        if(!arr[i].isBlank()&&arr[i].compareTo(arr[j]) ==0){
                            cnt++;
                        }
                    }
                    idxarr.add(++cnt);
                }
    
            }
    
            int MaxIdx = maxIdx(idxarr);
    
            return arr[MaxIdx];
        }
    	
        //금지단어일 경우 저장안함 
        private static boolean bannedString(String ar,String[] bad) {
            for(int i =0; i<bad.length;i++){
                if(ar.equals(bad[i])){
                    return true;
                }
            }
            return false;
        }
    	//배열에서 가장 큰 인덱스 찾기
        public static int maxIdx(List<Integer> arr){
            int maxIdx=0;
    
            for(int i=0; i<arr.size();i++){
                if(arr.get(maxIdx) < arr.get(i)){
                    maxIdx=i;
                }
            }
            return maxIdx;
        }

     

    2. HashMap 이용한 풀이 

     

     public String mostCommonWord(String paragraph, String[] banned) {
           
         Set<String> ban = new HashSet<>(Arrays.asList(banned));
    
        Map<String,Integer> counts = new HashMap<>();
    
        String[] words = paragraph.replaceAll("\\W+"," ").toLowerCase().split(" ");
    
            for(String w : words){
    
    	        if(!ban.contains(w)){
        	        counts.put(w,counts.getOrDefault(w,0)+1);
                }
        
                }
                return Collections.max(counts.entrySet(),Map.Entry.comparingByValue()).getKey();
        }

     

    - \W는 문자가 아닌 것을 의미 \W같은 것 사용하려면 replaceAll에선 \\으로 처리해야함

    - set에 담은 이유는 contains사용하려고 Array를 List로 만들어서 Set에 담기 (Collection끼리 가능 생성자에 넣기 가능!)

    - Map에 getOrDefault가 재밌다 value를 추출했을 때 있으면 값나오고 없으면 default 나옴!

    - Collections.max는 가장 큰 값을 찾는다. (entrySet > [키=값, 키=값,키=값] -> map을 약간 배열화), 뒤에는 comp기준 담는다. Map.Entry.comparingByValue()로하면, Entry에 Value를 기준으로 값을 비교한다!  

        -> 해당메서드의 파라미터값이 객체면 가장 큰 객체 반환해준다!  

       -> Collection인 List와 Set에서만 사용 가능한듯! 

     

    *Collections.max 결과 Map.Entry<String,Integer>로 map에 저장된 엔트리 중 value제일 큰 것 하나 나옴! 

     

     

    => 주말에 람다쓴 것 적어두기 

     

    - 그룹 애너그램 (리트코드 49)

     

    1. 내풀이 (644ms) 

     public List<List<String>> groupAnagrams(String[] strs) {
              //애너그램이란 단어를 다시 조합했을 때 만들 수 있는 단어 의미
            List<List<String>> stringList = new ArrayList<>();
    
            for(int i=0; i<strs.length; i++){
    
                if(!"-".equals(strs[i])) {
                    List<String> ana = new ArrayList<>();
                    ana.add(strs[i]);
    
                    for (int j = i + 1; j < strs.length; j++) {
                        if (strs[i].length() == strs[j].length()) {
                            if (isAna(strs[i], strs[j])) {
                                ana.add(strs[j]);
                                strs[j] = "-";
                            }
                        }
                    }
                    stringList.add(ana);
                }
            }
            return stringList;
        }
    
        public static boolean isAna(String a, String b){
            StringBuilder sb = new StringBuilder(b);
    
            for(char c : a.toCharArray()){
                if(b.indexOf(c) == -1){
                    return false;
                }else{
                    char[] barr = b.toCharArray();
                    barr[b.indexOf(c)]='?';
                    b=new String(barr);
                }
    
            }
    
            return true;
        }

     

    -> 진짜 구리다..무슨생각으로 푼건지 모르겠음 이러지말자! 

     

       public List<List<String>> groupAnagrams(String[] strs) {
           Map<String,List<String>> results = new HashMap<>();
    
       	   for(String s : strs){
    
    	    char[] chars = s.toCharArray();
        
            Arrays.sort(chars);
        
            String key = String.valueOf(chars);
        
            if(!results.containsKey(key)) {
        	     results.put(key,new ArrayList<>());  
              }
        
        	   results.get(key).add(s);
       	    }
    
        return new ArrayList<>(results.values());
     }

     

     

    - String을 CharArray로 만든 후 -> sorting해서 키로 사용 -> 해당 키를 이용해서 문자열 값을 담아두기 

    - 같은 값임을 비교해서 저장해두어야 할 때 위와 같은 생각 떠올려보자. map과 List를 사용해서 저장하는 방법

     

     

    - 가장 긴 팰린드롬 부분 문자열 (리트코드 5번)

     

    - 내풀이 (283ms)

    class Solution {
        public String longestPalindrome(String s) {
            
         
            char[] charArr = s.toCharArray();
    
             TreeMap<Integer,String> paMap = new TreeMap(new Comparator<Integer>(){
                 @Override
                 public int compare(Integer o1, Integer o2) {
                     return o2-o1;
                 }
             });
    
    
            int left = 0;
            int right;
            String target="";
            int len = charArr.length;
            for(; left<len;left++){
                right = len-1;
    
                while(left < right){
                    if(charArr[left] == charArr[right]){
                        target = s.substring(left,right+1);
                        if(isPalindro(target)){
    
                            if(target.length() == len || len - target.length() < target.length()){
                                return target;
                            }
                            paMap.put(target.length(),target);
                        }
    
                    }
                    right--;
                }
    
            }
    
            if(paMap.isEmpty()){
                return String.valueOf(charArr[0]);
            }
    
            return paMap.get(paMap.firstKey());
    
        }
    
        public static boolean isPalindro(String target){
    
            int end  = target.length()-1;
    
            for(int i=0; i<(end+1)/2;i++){
                if(target.charAt(i) != target.charAt(end-i)){
                    return false;
                }
            }
    
            return true;
        }
    }

     

    - 팰린드롬이 무조건 처음과 끝 글자가 같아야하고, 배열은 반만 돌아도 된다는 점

    - 맨 왼쪽 단어와 맨 오른쪽 단어 계속 비교해가는 과정으로 작성했음 (left,right) 

    - 만약 맨 오른쪽 단어와 왼쪽단어가 팰린드롬일 때 두 길이의 합이 전체 길이이거나, 전체 길이에서 팰린드롬을 뺀 길이가 

       팰린드롬 문자열 길이보다 작다면, 더 긴 필랜드롬이 나올 수 없음으로 return했다.

     

    2. 투포인터 사용하기 (13ms..뮈친)

     

    -> 최장 공통 부분 문자열 (DP)로 푸는 전형적인 문제이지만, 속도가 안나온다! 따라서 투포인터를 사용하셨다한다

     

    int left;
    int maxLen;
    
    public String longestPalindrome(String s){
    
    
    	int len = s.length();
        
        if(len < 2) return s;
        
    
    	for(int i = 0; i < len -1; i++){
        	//0,1-> 1,2 2칸씩 확장하는 투포인터
            extendPalindrome(s,i,i+1);
            //3칸씩 확장하는 투포인터
        	extendPalindrome(s,i,i+2);
        
        }
    
    	return s.substring(left,left + maxLen);
    }
    
    public void extendPalindrome(String s, int j, int k){
    
    	while(j >= 0 && k <s.length() && s.charAt(j) == s.charAt(k)){
        	j--;
            k++;
        }
        
        if(maxLen < k-j-1){
        	left = j +1;
            maxLen = k - j -1;
        }
        
    }

     

     

    > 팰린드롬을 발견하면, 확장하는 형태의 풀이 

    > 처음에 주어진 값에 따라 팰린드롬으로 판별된다면, 왼쪽, 오른쪽으로 퍼지면서 중앙에서부터 판별! 

    > 두개의 포인터가 2칸 3칸으로 (2>4>6),(1>3>5)형태로 확장된다.

    ex) dcbabcdd 

    > 2개의 투 포인터가 우측으로 이동하다 3칸짜리 투포인터는 bab에서 팰린드롬으로 매칭되어, 양옆으로 확장하면서 

       가장 긴값을 가진다. 두개짜리 투포인터는 dd가 매칭되지만 더 확장을 못함! 

    > 마지막에 k-j-1을 해주는 이유는 조건식에 따라 마지막으로 검사하면서 k는 팰린도룸보다 1커질 수 있음! 

     scbabkl 길이 3,start는 2,end 4  > 이때 k는 5,j는 1 길이는 5로 측정됨 

    따라서 left는 j+1, maxlen은 k-j-1해주어야함!  (위치뺀거랑 길이 다르다~)

     

    - 투포인터 알고리즘 

     

    > 두개의 포인터를 사용해서 리스트나 1차원 배열을 탐색하는 알고리즘을 의미한다. 

    > 순차적으로 접근하며, 위치를 기록해두어야할 때 좋다. 또한 정렬되어있을 때 성능이 좋다.

    > 연속된 배열 혹은 리스트에서 부분을 찾아야할 때 많이 사용되는 것 같다! 

     

    > 보통 left,right 혹은 start, end를 사용한다. 

    > 보통 left(start) <= right(end) 조건을 많이 사용한다.

    > 시작과 끝을 가리키는 경우가 많다. (위의 팰린드롬 문제는 그렇게 하지않긴함)

    '자료구조와 알고리즘 > 문제풀기' 카테고리의 다른 글

    연결리스트  (0) 2024.01.15
    배열  (0) 2024.01.12
    SQL 고득점 킷 JOIN  (0) 2023.09.01
    백준 15990 - 1,2,3 더하기 5  (0) 2023.04.14
    백준 11052 - 카드 구매하기  (0) 2023.04.14
Designed by Tistory.