ব্যাশে দুটি স্ট্রিংয়ের ওভারল্যাপটি কীভাবে খুঁজে পাব? [বন্ধ]


11

আমার দুটি স্ট্রিং আছে উদাহরণের জন্য তারা এ জাতীয় সেট করা আছে:

string1="test toast"
string2="test test"

আমি যা চাই তা হল স্ট্রিংগুলির শুরুতে ওভারল্যাপটি শুরু করা। ওভারল্যাপের সাথে আমি আমার উপরের উদাহরণটিতে স্ট্রিংটি "টেস্ট টি" বলতে চাইছি।

# I look for the command 
command "$string1" "$string2"
# that outputs:
"test t"

যদি স্ট্রিংগুলি string1="atest toast"; string2="test test"থাকে তবে চেকটি শুরু হওয়ার পরে আর কোনও শুরুতে "ক" তৈরি হওয়ার পরে এগুলির কোনও ওভারল্যাপ থাকবে না string1



ঠিক এই কারণেই লোকেরা ক্রস-পোস্ট করার কথা নয়; এখন এটির প্রতিটি সাইটে একাধিক উত্তর রয়েছে যা আলাদা and এবং উভয় সাইটের জন্য এটি অন-টপিক। আমি মনে করি আমি শুধু তা এখানে তবুও ছেড়ে যেতে যাচ্ছি
মাইকেল Mrozek

উত্তর:


10

যুক্ত করার জন্য কিছু ত্রুটিযুক্ত চেক সহ আপনি এই জাতীয় কোনও ফাংশন সম্পর্কে ভাবতে পারেন can

common_prefix() {
  local n=0
  while [[ "${1:n:1}" == "${2:n:1}" ]]; do
    ((n++))
  done
  echo "${1:0:n}"
}

আমি কেবল লক্ষ্য করেছি যে দুটি খালি / নাল টিপে চালিত হলে এটি একটি লুপে প্রবেশ করে। [[ -z "$1$2" ]] && returnএটি ঠিক করে
পিটার.ও

এই পদ্ধতিটি তাত্পর্যপূর্ণভাবে ধীরে ধীরে (লিনিয়ার পরিবর্তে)। স্ট্রিংয়ের দৈর্ঘ্য দ্বিগুণ হওয়ার সাথে সাথে সময়টি 4 (প্রায়) এর গুণক দ্বারা বৃদ্ধি পায়। গিলসের বাইনারি-বিভক্তের জন্য এখানে কয়েকটি স্ট্রিং-দৈর্ঘ্য / সময়ের তুলনা করা হচ্ছে : .. 0 64 মি0.005 সে বনাম 0 এম 0.003 এস - 0 এম 0.013 128 এস বনাম 0m0.003s - 256 0m0.041s বনাম 0m0.003s - 512 0m0.143s বনাম 0m0.005s - 1024 0m0.421s বনাম 0m0.009s - 2048 0m1.575s বনাম 0m0.012s - 4096 0m5.967s বনাম 0m0.022s - 8192 0m24.693s বনাম 0m0.049s -16384 1m34.004s বনাম 0m0.085s - 32768 6m34.721s বনাম 0m0.168s - 65536 27m34.012s বনাম 0m0.370s
Peter.O

2
@ পিটার.ও চতুর্ভুজভাবে, তাত্ক্ষণিকভাবে নয়।
গিলস 'অশুভ হওয়া বন্ধ করুন'

আমি অনুমান করি যে বাশ স্টোরগুলি অভ্যন্তরীণভাবে অন্তর্নির্মিত দৈর্ঘ্যের সাথে স্ট্রিং করে, সুতরাং nতম অক্ষরটি পেতে স্ক্রিন nঅক্ষরগুলি পরীক্ষা করতে হবে যে তারা স্ট্রিং-টার্মিনেটিং শূন্য-বাইট নয়। এটি ব্যাশ একটি ভেরিয়েবলের শূন্য-বাইট সংরক্ষণ করতে অক্ষম হওয়ার সাথে সামঞ্জস্যপূর্ণ।
পিটার কর্ডস

8

এটি পুরোপুরি ব্যাশের ভিতরে করা যেতে পারে। যদিও ব্যাশে একটি লুপে স্ট্রিং ম্যানিপুলেশন করা ধীর গতিতে রয়েছে, একটি সাধারণ অ্যালগরিদম রয়েছে যা শেল ক্রিয়াকলাপের সংখ্যায় লোগারিথমিক, তাই খাঁটি বাশ এমনকি দীর্ঘ স্ট্রিংয়ের জন্য একটি কার্যকর বিকল্প।

longest_common_prefix () {
  local prefix= n
  ## Truncate the two strings to the minimum of their lengths
  if [[ ${#1} -gt ${#2} ]]; then
    set -- "${1:0:${#2}}" "$2"
  else
    set -- "$1" "${2:0:${#1}}"
  fi
  ## Binary search for the first differing character, accumulating the common prefix
  while [[ ${#1} -gt 1 ]]; do
    n=$(((${#1}+1)/2))
    if [[ ${1:0:$n} == ${2:0:$n} ]]; then
      prefix=$prefix${1:0:$n}
      set -- "${1:$n}" "${2:$n}"
    else
      set -- "${1:0:$n}" "${2:0:$n}"
    fi
  done
  ## Add the one remaining character, if common
  if [[ $1 = $2 ]]; then prefix=$prefix$1; fi
  printf %s "$prefix"
}

cmpবাইনারি ফাইলগুলির তুলনা করার জন্য স্ট্যান্ডার্ড টুলবক্সের মধ্যে রয়েছে । ডিফল্টরূপে এটি প্রথম ডিফারিং বাইটের অফসেট নির্দেশ করে। একটি বিশেষ ক্ষেত্রে রয়েছে যখন একটি স্ট্রিং cmpঅন্যটির উপসর্গ হয়: এসটিডিআরআরতে একটি পৃথক বার্তা আসে; এটির সাথে মোকাবিলা করার একটি সহজ উপায় হ'ল যে কোনও স্ট্রিংটি সবচেয়ে স্বল্পতম।

longest_common_prefix () {
  local LC_ALL=C offset prefix
  offset=$(export LC_ALL; cmp <(printf %s "$1") <(printf %s "$2") 2>/dev/null)
  if [[ -n $offset ]]; then
    offset=${offset%,*}; offset=${offset##* }
    prefix=${1:0:$((offset-1))}
  else
    if [[ ${#1} -lt ${#2} ]]; then
      prefix=$1
    else
      prefix=$2
    fi
  fi
  printf %s "$prefix"
}

নোট যা cmpবাইটগুলিতে অপারেট করে, তবে বাশের স্ট্রিং ম্যানিপুলেশন অক্ষরগুলিতে পরিচালনা করে। এটি ইউটিএফ -8 অক্ষর সেট ব্যবহার করে উদাহরণস্বরূপ লোকালগুলিতে মাল্টিবাইট লোকেলের মধ্যে একটি পার্থক্য তৈরি করে। উপরের ফাংশনটি বাইট স্ট্রিংয়ের দীর্ঘতম উপসর্গটি মুদ্রণ করে। এই পদ্ধতির সাহায্যে অক্ষরের স্ট্রিংগুলি পরিচালনা করতে, আমরা প্রথমে স্ট্রিংগুলিকে একটি নির্দিষ্ট-প্রস্থের এনকোডিংয়ে রূপান্তর করতে পারি। লোকেলের চরিত্রের সেটটি ইউনিকোডের একটি উপসেট হিসাবে ধরে নেওয়া, ইউটিএফ -32 বিলটি ফিট করে।

longest_common_prefix () {
  local offset prefix LC_CTYPE="${LC_ALL:=$LC_CTYPE}"
  offset=$(unset LC_ALL; LC_MESSAGES=C cmp <(printf %s "$1" | iconv -t UTF-32) \
                                           <(printf %s "$2" | iconv -t UTF-32) 2>/dev/null)
  if [[ -n $offset ]]; then
    offset=${offset%,*}; offset=${offset##* }
    prefix=${1:0:$((offset/4-1))}
  else
    if [[ ${#1} -lt ${#2} ]]; then
      prefix=$1
    else
      prefix=$2
    fi
  fi
  printf %s "$prefix"
}

এই প্রশ্নের পুনর্বিবেচনা (1 বছর), আমি সেরা উত্তরের পুনরায় মূল্যায়ন করেছি । এটি বেশ সহজ: শিলা কাঁচি ভাঙ্গা, কাঁচি কাটা কাগজ, কাগজ মোড়ানো রক। এবং বাইনারি খাওয়ার ক্রমযুক্ত! .. এমনকি বেশ ছোট ছোট স্ট্রিংয়ের জন্যও .. এবং একটি মাঝারি 10000 চর স্ট্রিংটি ক্রমানুসারে প্রক্রিয়াধীন while char-by-charহিসাবে, আমি যখন এটি লিখছি তখনও আমি তার অপেক্ষা করছি .. সময় কেটে যায় .. এখনও অপেক্ষা করছি (সম্ভবত কিছু আছে আমার সিস্টেমে ভুল) .. সময় কেটে যায় .. কিছু ভুল হতে হবে; এটি কেবল 10,000 টির মত! আহ! ধৈর্য একটি পুণ্য (সম্ভবত এই ক্ষেত্রে একটি অভিশাপ) .. 13m53.755s .. বনাম, 0m0.322s
পিটার.ও

এখানে প্রদত্ত 3 টি পদ্ধতি cmpহ'ল সমস্ত উপস্থাপিত উত্তরের সম্পূর্ণ দ্রুততম .. মূলতঃ দ্রুততম (তবে চরটি ভিত্তিক নয়)। পরেরটি iconvএবং তারপরে খুব সংবেদনশীল দ্রুত binary-splitউত্তর। ধন্যবাদ গিলস এই মুহুর্তে পৌঁছাতে আমার এক বছর সময় লেগেছে, তবে আগের চেয়ে ভাল দেরি হয়েছে। (PS। 2 টাইপ মোড iconvকোডে: $ইন =$LC_CTYPE}এবং \ ইন UTF-32) \ ) ... পিপিএস। আমি উপরে উল্লিখিত স্ট্রিংটি 10,000 অক্ষরের চেয়ে দীর্ঘ than এটি ছিল {1..10000 of যা 48,894 ডলার, কিন্তু এটি 'পার্থক্যটি পরিবর্তন করতে পারে না
পিটার.ও

6

সেডে, ধরে নিলাম স্ট্রিংগুলিতে কোনও নতুন লাইন অক্ষর নেই:

string1="test toast"
string2="test test"
printf "%s\n" "$string1" "$string2" | sed -e 'N;s/^\(.*\).*\n\1.*$/\1/'

কিন্তু নকল এই
jfg956

উজ্জ্বল! সরাসরি আমার টিপস ও ট্রিকস লাইব্রেরিতে যায় :-)
হ্যান্টলিউ

বা, বাশ স্ট্রিংয়ের জন্য, এতে থাকতে পারে না \0। ব্যবহার করে trএবং \0, পদ্ধতিটি স্ট্রিংয়ে { printf "%s" "$string1" |tr \\n \\0; echo; printf "%s" "$string2" |tr \\n \\0; echo; } | sed -e 'N;s/^\(.*\).*\n\1.*$/\1/' |tr \\0 \\n
নতুনলাইনগুলি

আমি এই sedপদ্ধতিটি আরও খানিক এগিয়ে পরীক্ষা করেছি এবং দেখে মনে হচ্ছে যে এইভাবে ব্যাক-রেফারেন্সগুলি (অনুসন্ধানের ধরণীতে) ব্যবহার করা ব্যয়বহুল। এটি এখনও অনুক্রমিকভাবে বাই-বাই-বাইট লুপিং (প্রায় 3 টির একটি ফ্যাক্টর) ছাড়িয়ে গেছে, তবে এখানে একটি উদাহরণ রয়েছে: 2m4.880sগিলসের বাইনারি-বিভক্তির তুলনায় দুটি 32 কেবি স্ট্রিং (শেষ বাইটটি পৃথক হওয়ার সাথে) এটি লাগে পদ্ধতি0m0.168s
পিটার.ও

2

এটি আমার কাছে অপরিশোধিত বলে মনে হচ্ছে তবে আপনি এটি নিষ্ঠুর শক্তির মাধ্যমে করতে পারেন:

#!/bin/bash

string1="test toast"
string2="test test"

L=1  # Prefix length

while [[ ${string1:0:$L} == ${string2:0:$L} ]]
do
    ((L = L + 1))
done

echo Overlap: ${string1:0:$((L - 1))}

আমি কিছু চতুর অ্যালগরিদম বিদ্যমান থাকতে চাই, তবে একটি সংক্ষিপ্ত অনুসন্ধানের সাথে আমি কোনওটিই খুঁজে পাচ্ছি না।



2
সাধারণ রেফারেন্সের জন্য, এটি সামান্য দিকে। দুটি 32768 টি চরিত্রের স্ট্রিং (সর্বশেষ চরটি আলাদা হওয়া) 6 মি 27.689 সেকেন্ড নিয়েছে।
পিটার.ও
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.